From ad5d15887dbfa177b9027e0c0496a9f7c986f157 Mon Sep 17 00:00:00 2001 From: Jarrad Whitaker Date: Wed, 27 Aug 2025 23:29:55 +1000 Subject: [PATCH 001/195] embed ICC profile into thumbs with InteroperabilityIndex=R03 --- internal/thumb/testdata/interop_index.jpg | Bin 0 -> 540550 bytes internal/thumb/vips.go | 28 ++++++++++ internal/thumb/vips_test.go | 22 ++++++++ pkg/media/colors/icc.go | 51 ++++++++++++++++++ .../colors/icc/compatibleWithAdobeRGB1998.icc | Bin 0 -> 580 bytes 5 files changed, 101 insertions(+) create mode 100644 internal/thumb/testdata/interop_index.jpg create mode 100644 pkg/media/colors/icc.go create mode 100644 pkg/media/colors/icc/compatibleWithAdobeRGB1998.icc diff --git a/internal/thumb/testdata/interop_index.jpg b/internal/thumb/testdata/interop_index.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ae78303df4ca2c1b7f417c53011109be9fd501d0 GIT binary patch literal 540550 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
  • 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ö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 z zYo&vfkWyVTm@J)>23?lAdsGfCk#Q z>G;COQnt^nhFQNtP~}7b;`_f@1Nl3_UyfKuB8S$k?2P+R$Z>iieWp)pw>y?yOEZ2x z>*}J_eW$WlmYg>+XVjxx;>efsK-)Ds)ayEKtngYVP=EV?Y_B)*Vzq$?7@LYT_S?F& zXV5C%@IKyPoUd!WVA^67>U-+8@_nooBMff{R;P>)TaKq*3QLxTxt+P0SM4niZwUHy zarHUP-lDe4QTsDAPgy0%m{GypL0=eZ96Ep&FDs-`cjYfPktWhFAxx%^!#Qbx1NbL9j)ezLIJ1_VwZA~aIijQl;ZQ#3d>*Jy2CXE_?! z>^qk#fgV#@Xr#nFz&@%|=KV^+YLQz;^i24#?|9hyLYcOP7*8XRTbI9(nYuOv8&j8F2YYN@6U~+gBYRgm&j8XQA%H)xZOh48rqSmP6(Vq8IZGC5g=VU6Y)$**s-vvKezz3Mm^)v{1dGnH&VNY&i);aN)nt(4f(xjm4Dh z$}5g$YTV$s#$cs6@@`R4O|#F4tuEtqH^G*%UoYI}T=*@cMBIeSZ(-u19Fv6@SlP)N zCD}!wH3@(hd{S3t29oyyLx98Mn?#(3E;2%DQ&F0aLuF8Ry=jYA7{grr6?lFtS0lvD zPkK2x2TGcW+0CbV)HtzUmegSvQ#w*@V{bbr`javHJdL5MILtp>fj(f-36cp7A5f;* ztMcnna~@yyS02k(1=$Xwm2PG6q-K+nE`&lj;Y9NuFq_Zi{{YgsZ8AJd)ykfGD~Olm zQ!^SV)rScKyEXBJPn5{MH7~58!g6y&G*T>efH|tP>}LuyW$Wfygq5)0p!RlDONHCE?6(O20Qo)DEM*I2 zh^PT4v=dH`BGk>?!a>vv!TPIDz=-o9aseW$pHOB7>8(A9>rm{p__r3cxI>OPO6W}M z3j{|uqmzHQi~d?IF|954Ve01lIH?ynk*_&Hcc0$H4x<75k$)2Y)H>Hds5h~d*0mc) zBGPQdYjDtcKGbC9;4>V5XOH;Os@oi)#c88lIMq(@CpPk%Rv#i6DpWuo;8NgG*MM86 zeLe4qTXaL0<{o-!e+sXY_uz1q1Doxs7OV969j}x4Ki0J3tTv179PKx0AF`)^!J-*J z+##cjT-f5ygzS%zzRoNrVg4Zp{%}tCvigf#&Oecm}Ej@mZevP`vtxmKu z^l$y-H%q00?i$-Ih6d0ZNPENg-kZONi@jGJPkbT#t&@$inx|0 ztW~8gz}k@tbZJ>Df4#vuOqszkM*z$v64Jc0-xN)4)i~R(W3=KHcfTsTIn~?^)X;MI ziH41KX11_VV>H2Lo^h5)jMQ4ik~uP{-YwKr#+ut5q}XIh$#G!Y@T$36ZIdK%0Ovzx zh|F=TfTJ;PW1*D$@!O5Jvw^PM^F`LFA=higTPX~qDMypU+VO-sRRyf+yYQ*ww?Q_T z$yJ+^*z$-XZaZDmWNsR-PW+PQR=$(0{%L5fb4Zh_t*%I*+l_|`+aLHR|9qN))4iyn`cdY;t0J69-6r*oZH0w@K!RE13E)b#9{{VGc zu9TMXQlpBzN^B*DG*VoLD0`}js#Eb~aVZ^)r58%l@on*-k+bisi8~Rz6sLq4%n3Jm zgi$y>UmNW!ZO%LHN|AGS5%*X2qB&iCA1BzCsKr_!)<$(Vc(SBgUSdXc6#$;v@sYf4 zxAJa)j!SO_i|sCQR}nqMXAhIrW5$NA5lS-{7uHt#3o=B_UhUGQ0N^RR zKHE;!--QdgD}8^w8RE53adBIy&187!oNS1;{<5|;%$#o&<8GHqAtP~;6)ETT@D+Nw zM};3bZjl$lrE2Xr64|`PJVi}eBJttl+^FP+-rX)#?Dt5={wj$4p3*zSqEg-2sOoy* zgZ-2RP8j18nFNd@@xf4In;V=umEQ8;J}$i1tCdF7Hu*O33Wh2TyKg0GjO$0)%Lr0=pM-Kvgw)mnnz9p?p^-6hJj z!v_{aw#xXd{{WRJC5aROYjhQlstJ zoT&Z&s+Fv+j*bCj{wh^zo8G3n(CF<3BY$OTcudFHW)beV=TV}*YAH8}ns&%)J)TPu zsMD@xR933Fjt;43EVV_$=S+`(PxUL+>s4FjY(jVepRLT8x@rd`hi|)% zA7oac81;<9;wigV8VrceWPe+!O4DpldnxakJN!T8P&7TJIN}tq@e(^jv5P2U)Af`n z+J;rbGnrQR42EU)z?}N z2o=flj|JDE2aHO`sDX=L39VxCkymady>IJR(Vh`IJf3BAwZXbJ5+wDeEJD-caIwGR zE7*BdJ*Hq6Qqi$4?Aq|XpUO~@s-0J8*MggC@|9F@3%aS&4Xgn?;X$4fOp(xL^C1bT z=H2x?rr6sTH!19u%JXuwvTf6$TQy9W^TdxLR%nGfC|p_PBuR`(wI$ZuAtI`g=$8*S zm6G2C%X$Jb!n_lOk`ZWq)Gqn5q5<%U0R1Vq2#*DD9GzBy#W(78PKZxG%B9UFWo?x@ zQ#yh`yOZ=%H84LI+Xj;niml?Lvh-)bYOZZfb|Xwq_>*=?KN5`+JaE88G~hRhC#d6t z#iUVPr%@-UU@cLup{*6dHvE3C6-~0V{{XdD9U&e>We-pGW^lzy869HQ zu>t7{GZG^^*_d9BE#2cKQ-Iw#8h$BAk5VG;S0*FdqwO{8Q(-edY(#m;R9`33a{y_@ z8p{?nDT-~7X$=|AkvQ06!B!z#q?DN8osc90igA#4tp|j%$phRRAXkg&u{W{28F2TM zIk1L)^Ci{Pul0{chYw}`WWl*?xAvCDB|7yQF9SC6qn@>;F1l@&`y793ikR)TE#f>1 zpHiMbAS%&n-A#J*)m8V0_)+;u)ic9tpg+RAmFWVv!W;e2K2rYx^sTkGiqux`2YHpZ<`)b)7PGq8 zS-jzQ&yFMWruA-tjp}Z{!i&p&otT3awyRoEJ>|AA{wom&mg!u0_F5tG={B9zi$$e% z#iv@a*d+$Ul++vDu=_cdO#DfLaGEY;@Qb}`vq7u7*r%PgwI_J^3cf&%bRjm$TWl=A zY1d{SBCN=E_+k-@twQvZwc^O)3GBqMP7|$r$)`qFEgUc5UWTKm)SZo3F*akUtu{W% z6`BiV;h(&(P~y7q0Q;)lDai62laGM~oev4oR`$ z-m>#_qC~@6Tgc11xl9Qk?_CPZdW}F3L3e{+Q77aJ$CxS&Pq|;>1_pCF7T^QE7^@Y| zbZc8dMBK*%sR z!as65T#Jzo(wzXLQK}hqt)K+yiw^T4=1%)86Cm+R@qLdfxkQNFH-$NgbSlFq9o->M zGs%i3ByDGhB6SHeRF=XwM7~%8ui^JbBYzKT41hB93ND(=ll+N1Ga8WYWk5ZldbV zZ+s{mh+TbZa;e0lg%{U{%M^?uph!p9k|7O1R#9o-yjFy}?S> z7KrZUYlYwS$yl{D*6*w#zbVfA=%!bVyq2SlYPH>@PMlsQT`J?NBuEX5eKv#hBdd;< zM^#*^fvziXgOasgNA!&^^Lu=zzDys~Nv5dS@TaL&W15o_v@^D@5Y8NvXuRH~Y4Iashswd8h*70$tBdB;VJ4;iWidGJ7dB3Oq6{sr7gZAEqK3K zH+62>#}?|WiWFlwFz3_Lv^>zFscyBot@l2!6^vK*;_%cOThOuDV`X z=O%AI?IOFudBJWq#Qy+fKk}ts4?U^UNR_s9Q@0ypd7EF-qH0^~oa{T0eUetm)24UI z98zYW6uWV;J31?a??aiG zeCtMEDC<6r;4R{fhadJePg*t}#LN1Wcq8jH0^+gjEsD+4FE+3Y=9arjv1bJ|s6YizDJr+~dA^Pv}x@*7;1CTaUd(nR2&ya!2-* z4$;{K$CIuY22H0M2cl(gSL+Q*Ugf+w7MX5Kara#{50p;Si98b zRvlu?uXZ^s^C5dLW}bRYXYj>4P%f1nlXIbM2G;4W0;ayfGs(=R-y&Pdp>y6%-Wi#n zWi>g;jlv`Rs)c&Ex$;;2ig;vryvgf)^Bu`t!mQTVkq?7@zpS9@I~#g|s>>?wmo7qy z)&;}t_2ylov_|=G4toA| zN|j5wLC|`Jd@{K0V%0t_<)d_zi1(=TB-0S$ogb2SEqNMqnwnvqE7XE3)cl$z-S`*w zc57DZT%`sr)f{HTIM9>}bcyqtj5O-1(sp>7YR4W3wckNQU%=F`ILF^*bpqj4taU@y zZAM_zVeY8jS1t^HyNWL?6U+V7e|mGv9oYIJ2lG)o{{Zdg02nw+NNUR50!0ix zkmR)Al$LI{9fw*FOx|F~JQ;LWTzS)twEJ74iN;v#LvW8*c&SpUDn`gB;*)*uE1W96 z#dp8{n$vH+Xq@RAIV)beV(k9O_dGu0WWrNIJG>gj zHE@o6lhZ)VXPlDHs8F)Zwt>MFA;4;wrul<4A`8x*V4JrzAf2={_fnACtwqJNRS892 z4^%{R~Q`^vyGT)PRp)>QYts@4{_JulkXd_=P4Rz~LC zbnv$`+ZR{bP3imGd5uOKwp;g=BgJw)@$(vz>nxCiLDZ`!(NK$zT59tJra(fS{{U5X zxl9c8H4W~AHVNqFjX zUW2v#2&{I(^)d+T@J+qCfUiMXKmOc5g;;dWp_w64jrEYcPIWEd6}Ss<7VszBZPxbc zrN->CKlV{J54%pxO7-N@*>|}5LUmJKN;KViiAVBfrI{WTsH$iU6wv1Aw!gEQEv}fk zCSqBAoG3t6YnnDYPDRFfInp!_TXxfR82K()5zx zr)euJQo*SEiUp3_4o1^ZvRCVQGaQYsKt|Ioo-ZHtqH@&HV_embP-#mWF9R%@5v?b0 z)3*qHw%Z+> zY0%kkr35%E^SGx)w@QvrVw`F`tJ_j3`9I=TI=gj?zRB{ZEYbe}-b-;e3M@ZjDaQ8{ z#$dx8$GvJp(Kk!95`}iR4}(HX-R;IXW~S;`u}c622U9|lSU<|EKHOBpc@(XWQh29a z8s<|p^zreTH07G;*ym1VE+noTPK%3F+i$BR!}kkLHG9~rUD7SiD}$1WCyfb`UY>&k z!J0}Ysj*FPp~Wv10D-1lZ2tf(D`k_X4dG8WS038|QCcACLfaoWCv7&z>B}8OqgLq8 z#AlcP0Hthc_M`YohUI(7$R_A=F2&E@jsOijABjFS+u*INM~N7CRbF7$fk)yVN=4zg zZD!#A0RGB8tGP&>#o)?`$uN5(vq#oTK-nF;T*=5jw-xT?c(=7%WcGWq{t;4x4L;S@ z+tkvr-!@-jW4xm0FYzr(bnT?TdnFQHQ8pKBolIKG!9r$cM#7IRzycSCojY)x<2X8q zvB}D+K`9ZuPE~TVc9{PFA?8-Ad0W`=*9w!{zmZQpD|46FsctaaBqpcar^2pq8p@dZ z)`pLG^uv;+A94GN^Q1K8!am%#cqm6}w?t?HoQ`(OSlku?*hMD4&s_J8_M>1jad z$~jKI`$~O>Ro-4XEH}2#)-wM9Du<#TjQ4DvGl%m305w(S{b>`eGpun}4+1WcGRP;-IzZM)%>n;HK=5-TZ0v)sDm;l}y8c&p?Xp7XJYFtF$!V zu_Yc*Zu6%mz$4EkS(fnctEyI`)9qJ+xK=a%r#^lutI*tHI&BvkinU%` zHa8w?6zuC+v;_SVLkld>zigzP^TinU%(hi2h$HO(0G3x+&A%6IqfSDU(K; zb~NN%fW=!~InaT_Qkpt+3US9qy(#N>&m0~_XJk{Ba1V73nuSNVleXAzhS>tU*oA`I zqO$4c>0S4msTzvIn?5oSL_AZK2H)UG(4`e@`|jYb5v4+p*^k($L^0)eBD~3p?{b@P zgDkftb*XolP$Xjw+!|24uGnp%fn;c|QrN>}(|VeGPy$vwWKrB(sq;FZH)s$!0j!Df-`Ho}=ZmkSm^&R1w*CKVAI(uhS z>yaQ@Hb<>ZqfSLFlvE-u!7NQnnIXzix4qS(?=mdcK#(QMuM+(#Ln@)lN-OO^${l(W z#QiCTN#m0svrN6R&JI;f)mFF!O%u7OI-)(qGPZ|$h)y})@;*RK@OGK0Gy0oQq_{Bp zP|A)%Eq+x<4n-^;sAk)FDZ?h7Gu;Xf-5JGtJ8DO1Y1|X91JgLqblwD4y)hAt9(NzI zi^?U63*RD-S4{@L)OQv*LA8|$M7Pd%V^imns|~T^0^GGa))yLNqr%h92OD!+f3mM_ z$cG8Z5xvzMZqi|l&^$!Gs&?sbjri=9J6dT-UFI(^k-L^($N>ZN2?TaOe0b%0J0o z?0QMIn7GMoT#jYyR>@jsEF3L`;i_q!3v`rZ(_%)ZX#FP_$7VX!(yel@4tgXwGeyC= zu1YG_q^bq;QB)4T>heh$;@2@a56+*C0V}sOo0%)-1l_{m+c+V)Tx(QU3yG&D$QHVh zJ1O)|OCF;Bl=LZ6OE-`DROp7mb0c#IL2U5PkC9pz0y1rK2xvlJ)a*B=tt!)?IM^kJj&Y6 zs=i4fpP04!)80(nkzK)^`__cgRnu3Xn^}NgtxTICxkMZRYMm)$2g4@mc;eKXf$---BVf_s26&ALb$ZF_LZ{R-*ANbl;MKw+h9E?T`1T( zK%Nq9y|WIYR{Ly6a>Z6}^L%q!hJ1g~4iKy)6AC&6) zt3lpl%Cd}8bD=fjQ@Kln<8mO5W7dtHOwk=0_RG_D-uAut4HjdVc1MsSe$$E5!tPfz?`71L*)dcm!pU_+b^6E zG7+Qp$g?X&Z`St?!ZBp{pIhpvFIM6iyHzSXOf&L@S=8Lw4utFMg{N|g$vKuR=3q)Z zatND+?N!2bhUIo@tm-m$KP08G5w22Itt#nJS$H)obdyu%&4E^TapqB)X|wU&+*2-e zWHMo#lo%R<>)*!)Ro}_g%55`@`71tviwFIr8;PrUL;nD0v>>R(4Xx@DcHJq~c?C5H znvH{rreQYj_^4a+?d?lyzO`C%;-0w9 zV92(-B?jg)y<{S;8j2mbu#jo%>$iR(1*bmOs8lQU@(nvY$qWl0Sh>HY7NycWO{vA7 z?517OFUai@dOw9sm1=fB4LU&DlcTjo$i(X^hflTt09CB?#I`QO65ifk9Y2zG*K~u< z^lJX(H~agMhTjjCpFq=x`Bk4uYHdLy;3R{bt^$l(&Tw_&oG@^@rxaWn5!@^_}&_KCe4qeI#>g2$lP_0SQupd$`$)uhwf8b@?iMc=Zt z7+M{e;lzivQTnN<;C*YdG43Z>CeH&+M&QQc;O@?5m}R*EnWArZ*{2%AkryJqR2^&{ z1tPyDs?}305domQ)51k_@@`)3f2lI(lIbq^rCKI5o`IAZvhVdG@^PjQ*}9L(os04h z60eg8h3w=fe3(if#oj~Y?)eqad6E@ui1FH5IV0Y@rP-#^)50G!yPoRNx=#N9CjS5) zvzjq$E~Vu}9j*0<&ABG%k@t9*zei!pFO<2CiG&8?C?+Sv1oY$HA#@woguu9(0 zvT8da?kn@K;#>GjE~QOB67U_Dsiiwl6eq#}ek`Thqs1z-4nHc>E#q(9Ch^5ypBL%G zKB{C}9`_WDqKSiQ#{{HMfIUKnK^;asbVW_YIk@0N_fnJrb$4V_&{}BSkm39otBuu` zKsHCa{$$1SFvBO^C8p+5?iZ8h($Z*#k_Wmr!4CoVVoHtXH2 z@m@=>4ooK|-W}r0e7#@jh2fFx{gMdL8TRC)R@brIt^~+8%$T~wjT%syi({iMvT zepp7KVNbHOtjnuT%B{$<#X;21+-CMEgN}S~SBos8Xr#Wb)EUuQo9~ZT>Q`_%7Qm1$ z_`kV!6CcJM9KTLq<>IW`W|vqqCsLe51H0`i$B|0#P^~vdd`SMKW@6!Pn%SMhm+It8 zR!WB!^%`uKISE>|c9xM*g-5p*c5!p$alexELrezybX;8cRHh!Jtiz!Q$Ys-$BcVo> z5-d%}lMTVRGrbp?YHb8p+VN^ZHTG7!(szv%;O8%Ow-=-(A(G>s(RnDg71LP9WXHlD zWYc!Y?QF2tDmm4oS+)-EVxN>xA4&-o?ARwAcv&F9H*lnH1<+J^rn&J_WjVW=ZRcO; z1#meJ_;@E^fyUr#UTMazG4LLc$AdPa=S{h+;4no-g@08LhG7@qY>rDG=;1`#3+^ocO&{=?x`&VT$+ZZ zI8Mu;5~UHKsNbF8@GRyfbQ6^Ne z8pjNNl*Eax31nT%$1#bJ+2R=QDGeD@jDkeuobaIc zC#Y_B8kcVw*8pTmSm1@VtOY$J>UeGBr-WF}cJubVE7Fa!jq^CIrx zwCum#l7(`CA%S3vooV^j(9}Ruo$jK!-(uV34P8;4hdrF*dn&7?svt>fu_N@Uu3lfY zyGpl;wrtIhiJys&Kkrwew?~~;hl{)v7}pttj0mDXTZjG2Lonlda!AQ@aM52auq3)P zi^bk4&0#IT!ZR(0>dX9ir~|F9L@mK0p#E-O@~D1FF3-8ScW{sPm9IhRyH#>;)~L6aNvR~M39NeA^N8+JabbN5eutjNTunvD1(YBDb$oq67OtVB(B-A1%xzR8!{OaWJ z?BdBM^%UriZM{sCZA}Qfq>7116Gt(kC~dLc+IA&Vjcu((6dyXa;)$m8Yo&~q8&dq6 z=_w_XG#V)$MJ%tDQOt!;@-X{(c3BG9qfh z%Pj^)!s7eC{9>kR?MHf+0jC%y{?cM5xp|zY%@O+|rf8zOGlkA+ny~pXvfC$w;Hhqh z)>?-_S#PrYWx~AZJfB%0S8hCUFSM-Dn28@?1A=g9RmO_zjCykthk2IiRe%J`?z>zK z1Y&S=L|ZAFhi#MKhf0llxsY%MkzBO{og$_U{L3{a;dm#uOEm)OF)Ng!?@;EA#+-;i zfp9`Nd5I5a_tVm&vCy7**-^+DK?Yn4j`WFHQ|MZ+Q{Px4s5OWRx8CHyy!N^+PpTz5 z5HadqwdTy+V7rNF3rq{7s)fR-m=4Gd#t9QJ;TAH_nHZRy+3u7__;Thyi@jPnSUlj_p0fKpKB^p^{AyT z0bt>BaZ@Kpcq;V|BTh074`*n6LQt);#u&zaoSk1~Tcd2A(^c;!aEC@EwH2>qRa#e1 z4W4P-C|jG0Y}_2Do&vSiVLfW$9o}I-;#y+q9xU2YeLX0x zXDG6rEHnC}{{U4VMcNLW;zpUV+2MvQumk#&_N#oSu#-Rb0})U(C4wmSt+juI4xTXw znOZeCS+WUZi#aQgl)G~;XB-HN`c0(}JEP(wm%638#kh`)3k{H$ zzrw%w61ZLA&Hn&sPco;%(BeOT*-&+VpyRt5%FhZP#94pQS@eb9QAq6o-zh66lAC3u zX#K?clvgdbdJ{JFKT<|-@rRST^GpHl_Q6kiQqs`!;^j~5r@Xk@*{=w($y&Uu>HCCj z=U>{d&`GZ-`a(r;erOVNTj-!1nvg%M{{Rb6c|g)2?o702`#<+6{#NwFQr+whZv8ll zjjUPJuzP$s`$P4YX>t1Vp|=b;V*dc;mt(m~uP`kw8cP{6;3-k?9NtY#k51;1&UAkk z63xMW{y6?*ii{6v)_%3a25_TEzX?&@wzv`ErFL0C zi_#N!OKY+_G+7PqsL|>~Mk(&z&;I}tlJ+t2T0*p9?puv@Az`ugECD$i=}uaK(#(|m zbsF~+2~eYsDz`{ndx=oR-j6wsNSorj1j0Ti>`}s&%YG7a*Ek=-I3u)#;@P>e(yo3> z+?#OiHt_I8c~Q4Wwl^K9&8scE?w25d_i~Tq_SXg;-Byac$+>~S4xm?SHdQAc=2C6% z)s8J1(^_;wj=eN_6Kp_({>|lADX+*PY)MOQL+(+KC2Dq5NW25xQ#CWrW>Oq8F)vVZ z3Ts%%g&16I+-uFr)UMjb&Cv3H3?RPhDd-%Clm}1vqb;g+@ z&+8|(3D>$PaWxxM15!z;K~ekqmCse`CQhc=j?#&&ZreuO=}S`9MExfcTP`a@y0$$q zx5MG}h(FeX&D-I`Q4fff7_~k{D)~vaGZRb zXqC>|?CFDUkzbub(YkIFj?;d7!~00}y^_IFi(re#^Q{qlopQfPW6^a@tFExrvK!*#Q~>%*nOwC_pqSwg zYOX2iHwV3|yjO|qRj1BKxN%%7UsBsTFCn^Qu6Nf?;@1{1`Tqb-5ok>}{{R6Gll?Pa z_hl=TPL2k1Ymex zPY=&kZt{$A{{Y%C0pHG}`CqkFotm~``>LaB%WSvzB-}rd5B+Gg=DM|ye{KFcinZUP z+&yknf_eioDaExSImPNv-0kthHyu-T{-1>A*cMezn~y=?VP9ct%l$hoTiXS2W<8pf zVe@dD`(4T_Ql6Dg$x7y}_dCU-N_Ls7Wbpf{(XF9gJPMuSD)lj9G}OCK1}qlseK&9id{YL_@_;9Uo$;DxvMua+xKK z9bK3ip}CTia<@8r5-FM;AE#&C#$0wWH6|q6dA8QpK;qKY(6`VZYT1NY$}`brR-%SzW9(tm62+UCKqd$lOa!$U=HxV+B9Ew;n-cmDqXTC?bw;kPPJ zi;{BN7Bd~0i1>o^Qw|4&UH<@C4XA4!)=0~j^Q&!KGib?-zCZL%KVL|bLe|^AH`!Na zVb-;C@A{R_)y5|ppM_5KZ?E>kD&ZonN3%lPWIy645cN4r&U>xxrnJYT zWgO`>)xur$X|oD10{;Ms{{Y%MYjkt8 zUgrUx{%B`u$hG-2LwEC;V^7>a^-?u`#v|r7cEPW#Bj5Z;y&C=yzYZb{)^1rT&+=#F({+7 z6Jb0+AyYvLzkN`PIyKRvctts-c$>5K@b2-U=nf`1x#_g>)=4e3LRD zO!i6kNs92I)VCXh_n8g2<#EcZg?(#>(zZsbvHpZi)8>YCH@@3v>X7k7NfJ|!klxXvK8eWv2;fN;-)Ka^IBK9k%R zMUB_LTA&#}J+?dfTJTY~S z+YUOe(?^Txt3Ty2Rbu6l3t*VE%y99L(w5YC0h@dT)3YhZjC9#L;*AcEj#ASnYqk#X z4pmWgIk^O+u^Xr2MdboaXLlrYCiVN0rS@4*kSpUY-*o^s{{UKrVhb&!iz@AS-j$m) z#K-xg;VtYyK7ZHw)EiVQOH_KFLe&lfvFS|ud-0-t7@1@ld17Oa>*G)LQ8fJNFo3Y| zw#WFp)ni<&%~pJ04bSKFEBxt~S4KpU#Od=XlbwXmXAxD4atH(A_m6%{NSl;sMb~^U z=9viY$l4`sZa78QYo!B1QvM=P2H+KrQVn?WByckBQ9!(@qf@% zNEb__eb%4b52~%!P^NSz_YHbDnCm5bpKi7>xh%LV{l~Y=Dzrtn`c)h%cM79!yqs9hloJ65n$7CuCPo+~omUZSQV& z*+hiMp2(AO8B99Myoyghf`nBm@?tbqQCWLud6gU;-Qx0+Jgc-bz=2zRrBj~OPo_Aj z1B6Nfj6NMyUv7_8QoNWnUwmU+&+M~zu)k`X-IS(}d05N8OR98L4Cx|`VnYq5$ zZHX;IYl*N@fuYarDyc> zcsR(2eWi5L_GpoNTt>G8?Ql(L8yuT)O^hGZty)UgIi1Q-g#OZnq@l}|*yNcue`<=? zqR!U#R=LCr(RUsmH+r{>fu_@fgR^6{`&l7_3_X}Bki^>KMJ8>&FFWol)~T?`Ev6s; z02Ko30og$gD3{t$!LVsll~#rw;+v_G5iu0_jHvr2p~eWI%7#}H=tB1u9%Ndc87$M> z>E_rW!Q({i7!Py4qrSwT$+fkLaB@yGrQ4dA7(Bfe&@;2PrCPahp(Y!XSsPq3qK9;m z5*KA{Twgc()7OaPeVjTI`nFsb$}};_JABnQw|4T|Rg*3({>^;l50B{B2z>$LVxf0<5n+>CCYU;P!j8ycCvm;LxCT7KCAzawM4MYeHg zIbxZy#@*fpHsZ`CN;_$((BQ{AgQm3~l$`6e5nV3w?ym1kE-QF4?-X}d`dEzE{{UC` z@l4-tY;r_AYF`a0Pw-k^J?7NSCCz+@s-j))vYfyPFfs+78EOmILTjmM?r*t5=iv;KSUQ`0ss{0OCL$zi!aBwgPfF4+8h zzhy_a!gbC79v9h8ybhOTZ9e|fTL8B5qT5Y)MVhH5x|V-3?)9kGF3uQKQx`~Xo+hL} z2)YeSX`P-$1+i+iFibMItIef&HA7uD!O<>lOZ?{{WR( zUu~C;)bW2#DY+wLy7v_*&)HY#eVz8NZR7TpwphxD4+4pOb%Dk4OIsMJ%~EWaee_j$mdTZQ(^1+EcHhL) zdV*vq{N5^uaB5>a^;`s#+b!fqxN$`Zk{lFR>@;|Sa)2!EA~*%@%?VxQdnF4>Ntpii z$=~5pH2uCOios%I?|d^7VdY8sstH_A&)}Opb&DG~P&U*(RC4Ckm;FSogHa)n*yIZ0 zg6d9>Xl$AS?^gEA%#L{UlDaV@W$sXZOs;X6h~|GW zC_1Xs6hbQiLx}Zz$*J{mmZn1@{iOu1mr=#?@Ko!I7RddYIjgi* zORKq36=-iaBDkA$Pdney;g3iRfozC2Ex68``y(hqtnHuR>5T>8b58?zu;^Dj!qBgpbjUD+;$>2_Ra zZ<#Z7VWxJJd!$yYn35Zea(6nfOGo*YWVD%Z2yz8fHBL6R@zMsf%!EPT#B{e zjle|Vk3s}g;HKRm#`dJKW@kIXuNCxoZ?>tark_Ie?a2QCDxA1XF0);Jv_PEhilM!< zi+qkG| zYMs{_#s2{J!&U&-T7b&2W*$GK7LL|rS-EpiY_*3^8^*8JB2ul?lo)N|rP-RG6lXe< zx)zepi!um@?5O@v_Dd$s@l8S077@xjwLhv#(RYt=YjQuW3#g4^1=ORsQA;sV@9^o| zOs4O)zgnB9V7?eO(sJ`B+*F%(7oB+D>Rr9T^%|Kb_S_BT8r>BaTHKwxgc&gQ>GhVC zDqNs;kp$ho=G4B@CL+(-=KlaPxXP?YaM;}X%HZT`J~CQpzlZ+iHrZ;uM(ro<$Lmx4 z1wndsfS9}P7>-=RT`JsdONZ>oxb){2KtMGfmF_0GS>B?*nYvJ-mJ2`@b@UdXp`}+#HCh$Mk-G z(48ZCC1W(^Mf#KrG^x}nCr`~vt9G8ZTMG-3@paEtKK}sfrptw3my|w&O-#3H1MIOV zhfmwWpyhhhNHw?kQHzmW8qD_isBJT3gF!V3(vr?bq-M-~V-GrFl4xC`M=!*V;>@#k zqCQ3G7WOL@M+*uNf2E&;%qO}X-$sVgpc`m(HWgfam=2)@d5)R_<-`l%8)#ziXxS1J!$cI6p8sBP?ZntNN?m{01NR~l`wN)?`t zhH_UH+gH$HRk)j_P}!yXiJKgv`PZQ3BDA<2Vx4xZ^rM?s8`~vsVW}=)^SG%qLakAj z{lwM&8$4KhtxWQ}YGmrmY?&$ESEe~lxUsl?(tle^l>Ypt1c^PbT$IklsG>rl`9kb5 zJ0lTkf`zQ1!h3o=Tz_dv)==ch?Ig{AiA>k9t}N`xV2A9Ihpue!;P!3>{7TuZEq2Gd zMs6S4?^1Pq88W>f%cMV5LD%+Y`=vdH?5kx)?AG($y@&b zomHb#*)Op2Dq&xFKT837X)2Ft*hQRaJnHwXwJS34wl}yRWjU;M>(%^U`WBaosmATw z$*e@WlU~havO-I#bfF!@^K`*cz0YQhFicz~NA3br9kpGD8-#VE4XbFo0n_GJJB8Y` z`5eLfBh04yM`Sp14zX-aUIsNwG>u=o9Fpu?}VS4a@xo>Z?=z1ywr z!r!R(qPaO1k{XF#6)*5wGqTACD>K*k{%f~Dl??qBIMjK5va?8v)W>98-0?%%txEXG z+j+t6>h4YIdc<*{wGQ78=l=lOWN9hd6;xQV1~GfaNxN-HhMIS*Rn{X@i)r7vD{VGe z!z`B^Rl)7CGE?SBwlM8PIGt3RoQH~n*K*SKDx5AZnzaSL(>Xhr@T-fq=1>0sbwzsH za(mJbo>&^bc|ft*E4LM#-ddAX-gPUvI$LdgE6JmHYG58bd1|%O?Z%b7RQQLP9pZTx z5OW{1-Tn(kwSAo?vv-edKbmgks70Pc{Oi-hgYKr7ZoMLeP;Cm&B}IzGL$l18(1%D# z9=8*EHQ)HD-6mtu>Uz~NuB9HcBYPSy)jQ{^vAoGM<06ljsW{r&LfULNBWpFOXUd50 zW?+>yL0jT>SBQz^?xdFbD~-v(!!+T`ciTFgjV8XjCZOARcZYh8^;tZaXj7Nl*Zfsi zZ&wbCiEO;|t%|1YCGo0QjCu_YpORksEj1(KOzDO{EUBlVFvQOAjMOM&Z!=*7j;z2c*gPCrxg{tod$q^oo>`# zuNrrHm9lL6WBsn&_6D@imj@X69;9oHK9O2)$`+l!axo`?{59_X02Ot(#;om{E#apk zVfG|Fum-T;gsLh;OMJk>V>v46Ej~T_0-MuzA^!k(6yBe)kNdc)TObcZAgc~DeIUC# z2ks$dq4yUOC7BA{08~M|csMC3IPwKeE9q<=@nzL%x9xl%~`IOB@^cj7dJe2)X z&1e;A+*CK(d5kx5P$I>HO3{JfD2g+_;&p~UBD*P)hTDu>E4s#5sP%~ZLhN?2H7sKk zQVD3S(cOWBBu>98? zB`&k6Pnljw1ggVF#J@7LGbM@LxxQ)w)4I~*b-Q+IvOj0kqcy>f2tvO)CYvvHZ^4K; zIaPVqw)m}J!3|Z*P+aabI_2Rh_Zq>m3qO^w>so(G!@Ij#l3cC>-|(krF>05Uys>JC z?wH1_(EHS#PVC4Sw8<2o>0B=~+}dQp(*4RFkJK~0VTbAa@lpJm)=##VPuWo8S@fI3 zD%>XP{{W}`70%bH^>gJV*!yGkm7mD<=2>pO=-{;Wk7n0S7^|eJ0By2@i>J?uw!+Kh zGI|pYS|ocRRgGz&aUe|GV^!Uj*A>N4RA{ho=u_j-=w>vcYDvehB4+;pA+D4ynm2r3b&D7mfMNwV#`Or9r{vwMCMCZ9hMAJa5bUSN1^T)>gn$mX~jnt$^bC|v_^g>lCcloPa zQ%0({D7V(?+(zarc{uF}YDs4o87%cE8pbq>X}o^WQ@YA&#UMg{l9Ra8rA|D=rdn8T zK~x;guZemU;Tp+`UK;GDBq;cL)R^s82-AJlX}Cs@J7`=X1#i5yDXf|-lQWrhcDVHJ zMIIl*3V&DY9}e>Q^4z(_CN=QoeTgng< z(K6w__;_NgDm#9qdfb;3wwqT-ksQH-<-tSKO`Fx9a`$~%7kA&@?ow$G#5*y1llN<6 z7YIwOT5i$i7A!45(}$5_oTIMb%f6+1lh_Kpf5-3Mtye(QFxrVM_&PQIg{Z899-XQX zbNt^w1!rcmZUn&reFgM?;YZVLFwjw={c8H`h>}V^?q7fWsb$z4Xd3pFuYG=m)Yx@^ zP%USJ<=TAXgKmUM zUzANnE2G2x39FFIe{%HUk}%qiHMhsaKRUKCalOddsPb`9^u44i08MB1mE_~YWMQ#N z3Y()O&1SBF<0IhtmA^{X6|)Hg-lALLG}#6VU0g0UmchGKI=$s+w(DzwQRfc4KC;tR zs#>C9Aol9*Av}Fb z#d>N>8B)K^Nt*3iO}Kuw+U`FZYHZT=l5XWCrAyNR)|tAp=x0?f?8vrdQcl0#yFeqH z!2Ieh-v)dlL3D9e^6h^nC~l6Q%ygJv0ex)&pPD!W2=l=jo>!->)brHTX_q`xFNIj{^KTONiV-y5#Jw34 z;w0U&7uHmPYY%N->O{?-ulqnC~LR0l5B7q_I3C&Hq@l`)*a#Z9rvZnY;V`%298x|L#*vD!(C z*s-~`o43`V#GT1yEv(slp-3pkI5^(sNsD9iY1!NG!BLBSxsR2*On-$7vB{n23hS>f zG3ApI8O2_2{jxIXaN* z>lhP4DDd$G%6LqGy6H|LL;5PwII#M9>D(Rz@JG7LN`zl?s5DryoaL1uX=}vOG zp^oF}o?uek`3kR?tBR2(jng_N%00mGC8%F&*4E)uxVcqk+oh9rk{v0CZ^N&pArFI5 z^?XZB+^crA=1pRRS5h~cW2rfFjZTHDHMCthZgPEnY2Pi{k6zB}OapE?PVdg9YmGA#s#|~J zr1@jguy1TH_MlnrM(S&DzD_V1+Xqu zz}4E;<1#A97jW|ZWa9-6;kZ2(stb65No2*uj{D$8ss)F$2z`Yz8|c7w00 zZ~BEzBdp59A;*O&^QlfZ1`E3mb9%phf9+CX=G$g{Zu*h8a*t(%8@QOQUpj3fR+CGJ za{7sSR&1FfaeKZWixhgI_$7~MKi-l24E6QZDyA%+3 z$oY{)i4H;Rhpk_2PcZ)gX^uS9_p=-PJP!pdYlj^2Cq-mSD z>`8ZE)JIy+2<4qVm2A}gj=i3L{zX>xbQZTu5d>PWqSta`;y{?!5y`WSV~^kQ5;?b5 zsg+&Z1+!~srqUAba`$2=eoX?E%5&;IkFr5vs@r}t zlNuy2+%@S7T)6dlm-reSbB7rit4$8)Y+^={NOyNnLdhZg!#+_joHrX>MGbxz?RM{p*GH>*Bs$-=4Uk zgJ)`=E0@jm4}bWp!%e!wqaD9j(zKnut9~27#Qy!uw^AW^QStorSF6O!o5Myr1zT?S zL4fQ~r-nZ|b^5y)oOC|_0GV2oCk+H!KOK4kTQnGcsSB6kGzlX&@-C{Bzu-i^k~=T`SoL57q z=T`blxIumZH^|!4(0e>hsGyx;=337b{POo!rPub0Lz^wRy6T}SD3=XK#jBxz@vU1dBgbijnXs$_SoW&EQa8S`(l_bw zE|q>P;Ct$=Z?1JgTpEvTlq`LvCjEXZM;E2|*#64XGVbp_b+|kw_Er4uhj%TO!yl}* z!3%mx%9b!B-sMD3cOum&S8`00j^{Y+A_D!O5qv!ts4*vkZ>Q2U^ZVfWX z4PAw%ZacDR$@l(#rE@ZPDY`WtKbdK`apc^Gv=-(oR9qUZnyk<~Wk9~aTEGtW%iUT@ z`1Y&)Jv3Z`-FPWR4oEWv$GKT7lkZP##~v$5Xol<|+8ivZt`W)RITTxzO1f|!>je8z zWr&z28*jCn3A-as9t|3Sr|b~Kla>Hz^FHdA#G+g6 zZFn*=bpHPU!ks&0R%qdp)FV=kS14of^sB_Ce;cna-{|1CsOeCx@9rqK7GgQs=EmUV zK6O6la=J>){3a>g_xe@R3v_c9z2D}vUZvD_D9wv(?!+&@--^EWULUa;qFYUrLvH^h{dz4*vkZdaTFh%H43d z6?upM04hmKzT7AjcX8Tv{r>?y?<*4qwhnw&6C)!$~7 zFp#8qPLKZT!ja`3?GzY5S#03gl>Tj89>33m$sFtQd z@3f&gCLNf|v*7UuPu5ysFAm&4ydZ-=S!-I&tk#)g2WkZMTZg9w=xlqw$!Le_a@~8; z+bdSchVAzmR`YY~E923lY5I(&vTNZLplfP)-}G}1xAv9jERrwf+H=eHmz0L68dnN^ zR1J4milI45(Yhncg7t9YRhP}hQ0re>p;bqi<4%jC^D8fvr$(6EnEn-}wWX%{UJ#t% z;HIvR7g-{9B-VChr!6l=rt=}lq46BIzsiC`bXRMW6{gyX`GdI%WvI=bWTRoUwI!US zj_i6Xj+WWy_eEYUO!j=JKY#qIY`Qe{Aooi=1iI_w|2|57MadOvTbJ zm0wyaQF%GML8w;peSD^2l@%GU(~P|3>_7z>173e+05>=$P(kQ(RX%j*71Pu#Cp z1{}8EL!5sp^nG6BHr67uh!U8^C!Jf8*Gh65^b&8h9?*Qr_l(=Y-|9@mlWnJOpyTzv z{{TXnZ;y0?=}H|l{oKk_B60vySmadt71lUbWlTr)D+Z{!*hgWY;?Kig^#es*V>2%u?sr)pk;=AjS)B_R8F)7iKgap!~s@lllUSAy=vg}5)WUBq|S)QWxAz2%ZI!R zP`So)O*7s{kNK2KY*=wYh6lfby4z&kK%+dS*qsY?8y@4O70wmHK(QVNa1XA(sa)Ef z9J@Por|R{sAE}|)0oJY!adiFfqyGT4E@O8h2ioc2n`4K1fu-(~{7O@iGoa^CW3u}FzM|gXxTM_k@xNDDyrduRi9NLjYHNf*n z&0ggi#L4Kmaa;Iu{pj?S%Hwo)6|^#JeeaohHqy@Nx|36%%ZlFN;qgW+J6uY=>MIf1 zgkV0|F;i_qJe@(y-m_YgZ5U@46TVUO-I<(Nrf(_URxc82REgm$jo#sQu5;v6kuhJH zP~LB(mulnf6XQZU)m6HZqZuyAoja6EKu9hH>Q_*V+{0=)@fs{fy(G4{cjllzq9QMy4pme?nFB=UdTt|?MG?SE$20D#x4S;u zB9kGxiHg!Uy6PJ^OFwPzA*axwT)!alX}TJc%?LKt=J6KUaqJJV;Hg^D#N9@a+agrf z^4PyP5?b(F>Hh$gX{oMU8V6lIMLF={w65+(lPp4pu6D)^t_^}MJ`9%>e^b{2=Hm0aT-ZK!d5m*qavdX=|Y{U<^7^!@&S@KPQr z(~mEo=~in?lWr|QZk1P$?exoz^OZP{Ab+sWRGm1?`TeBmg4Ek&Kl?nh^m^g8-9 z@(-ieRB&cMXk|J)1K?K*$UYM-`S<$xpb2s01tv!7T2DvCtbW%xJJas7mm=I~R`yEe zBVnx7%-)j}N;-jZ7);=PwHvoOU9I*xl)Btp>i+;TdOSMvOOwy_Q(9ZKSB*WAZ%$g+ zZNPNXxQyRFPq^p`S!Q(WXXfgk!STw9RI8MF@KR=N9( zY`oSafSUKkKK3-pOOR$5N9#>h{@854>sRNewAM;kvnCdJpp6ZtaH*)Bsi@_(>if|d z)T@E_A%yEEM+?-XQiLv6i%9BL;yc)9IgLHiQ{h{$arWFU^*ck)sqDsSMDkfJ%H=W2 zHii>0tZmyt?8ryV!RV$k#`nK6VhZ}%2r*< zM49dv$%mCS>8@6ZMy3?zS6xeLwY9qY*s1iTN~4KdcM8!_ zj?tw;jr&Y;Ru^vf8?NQb+%yl1m1}9P$E!>;2edM*UKXFD@uTdSd#cp?bMM1RHf^%_ z@mlI^D6zqrad)y>I=0_J_L%H!iiuq#KT(Ob72W*Sx;qOHnk&{=k$z2RfwMPnaP4{G zUlNfQO3A&G^_1&!GwEJZ>p|0EA6X9$2zV-waw=oZRPgOPxkU`ya5E2DY0mmmzNAf4 za@p$Du3D!KJkEllKr;U&COfX;&F zqS4k%&vgzSO^$HXcb;|gvMf%oL_9j2J*H_>4twE@1b#vs!;SW#f zO_jZELzkk-C>e=xtR_)>q{;Fk9+Uovl`F5RmbF|IoKhT?ZIxBuOmfr7A z8Y>LTbQ@zpQZoH&UG=#{=m7Ml&X-tDjBZ>$s*eh+uzrwhI{Uvdez8-bNOgge9j*qT z>fKLiisS~sd2&#-UaNxbCV=<@?E?|%{ol=7FV>5NtJ4w3nObi!rJpIQr89TckCp`Q3Mb%P94zNf<@!6-y|7?#hZ`q8vZOn^mEk%3eyECO&sfErcK0jw z$w#+c-wcd}PqsyZ-)XqIqE^`{ju86PizIpR_NOg#7Lh(l8?1i+05w&T{{U)*rWuP7 zj~=~e_x?&vDIwj;`qOaRr>?2CYhHz8h{VZ59$i&In9PVbL{3v5&X}XscNbBPN<3Z6 zxT0yO?wf$7PVKEc-}s|-;r<69ydiYC0U%pNS$yKedP`CjV_gOS5?9e>I#vo^Y$&P zw7J*DyEJF^Pw($5y^i?+&$V$SQW^&jQX;C$!_6N(cvgwdUgaM?D2K%3S}r}v%iZDJ zfr_8${{RHVwfTp%!m(S5-aBgc%hQLGp*vw}rIke%pNwsHrZm7j?$mbjtyZ1#XK#$( zZ@cwV-TwfpX}-)@kEurB;&GvLvdIj303m2cy;d_$`sk|_pk$~CsBF@_XdUV6Txb6P z7!=edBU5fWYPwC12XK_bfBcKe4Lu`ipfmFl^lYH+FhyybPLN%ORXV{G2}RwClGBay z6%#HLcQ}e@vK`;2)_{(*!!GpUxk2ymqG7`I4OI;5)$2s&HO**C{cg5FH*4b$Di;<> z=0Wnv)Cg>kgr!B4GR>jrMGt8_7^Y=bOS>}L6>}5bC~BJzpsji*yB9w@~ip-^#1^-^G6bnO((;pMYghVxX0$IhZ=hVT($iR zOSvt_w{8t$3IWG_$e1pbCdLoDzG~+gm_9BY!|!}5CAFK(zC!N!g+p$ASl1k;vjJC85LVP3hZwa?C+iE0p#swr5@}( zn9LK83Yq01E~22fkGtyn30ZEXx>Hzl7lyx|y+5fXO{WK4R|ugr%8o6HfZ{%+&{m$& zMzu+rRVc&7Jz%=E-YnH8}rdN)f#UFHs0);;K^+LNr2|a)~j+;RNiE` z0)=z!Fz8|LC%1@k6;?)o>DS&OES6 zBw)=SnG15@QPx;51;pgJap87a(;8Qo?JYBY8xLp)y4=SVd6iO%PQ@;Ihx#VdYSnJHo2O*B2Z>IT`oH*7bTp>ReV1bCfmP3guh1HSLJRBv*IyD^JSll^3)H$nEN_x>b1Ug8?^ zE(BjqhhHr-?z$g1dgy&pgI}X^`neim=f_?uH#kz0K(0} z>-GNtinCvCmPach1DKEB*0k4@+jYm}*FIN%pLH=Mxh-bZP?yrGbfR>rLiMV>l91;H z%a5{Mj)ohIC*?*9fiUr>ns>H#sGmBs%68~P%${$BYAW2I*^ZEhy<8@07Ar=NjVfu| zRnvyWBxHY4TrBh5VaYNY{r%{zGND?6RGw3){{SynPwrj5==@H{Z*0D`29;E?UJAH` zWBwYU(-&y2wM&ymEf>N$eLh!#wGPS{|%khIF`risGR7(1jv0=AP?`o}* z*nOi1ebs-9+L)&|{{WJU2IFfgvfOf}KK5Ly9lAt|eO{8~d)A@1gccom&bX3+c)81& zy>66ZN4=)3M_V691To&n~6Ed1~c+o!?-0CG(z*hii zJ$Hh^?3wqVf9Im#`W4xSE>K;F;cbG|w(C>SiaT;VWpB5#%xMia9PWGH2%(JK8W`-C zD5^I~bgI&Y>q0aIZnny}SHm7(rCIF{Sz{FllvbG_&E)lCu}r~KxPqmgGc)^e^(X6* zWkornmg|ulO%}rf$IvK-MHx+!OIn#n>{{UNE#*qoH3#Prw@-t(DhifiF z>QJqh2D5ZaA5xbZ+U#N=Jdf%B0Lr&XkypWoJK7(c^i^EnVq;jJSgoo&b&Pc23r^iL zVViY1gAes3bb;BXxfRcuIc}wC#)=%Nme+HLSk-wpnKI#~-jO=IawTxL%41Zt(;mJ* z(yH}X_Nb*+M_qGH+H8`~B8|aK(3aPWIPPMkc|Dvo+>TsRx^nppx!X@dn%#MuK*RJN zl*xof#o7;gq_&H{B}dx$)h5Rpa5Gk`U#3q8zRg*zmitKJ$6BI@D^dtYbi@=;tx#5; z>Y@@abXPelH&Q~~Yg`qg%g4o#vBSEEn`HZ+UQ+G*U=u{Tkki4Z6^C+hj$F8-^{Hds zkSSu!mhh29PGRP%v&YoaWi`0*58AD9 zR{h@3Nt+T{grL(+NrNkN3TVCRcq=Zu*UiD$T z#J0+L*FJA`OVt*5(sOs3srlCH>5C@Va;R1@h;CMu{p#xr7@`2I_Q)66n}Bwh&9OTvX-*2OvKt8clu_m9d8(l-XDb|#_`Q{wki^~^Rs^$N(*JTKJ?LepcIt+9Si z=r`df?eW+Q*-;}!mTop>(w~4;rb=v9oMFbYS6IwSkG5(anY>!2*6Rd3A*ze0FR(5> zsqTeh*Wq?IBRm+qKh&X_yA({+>bUQO2)3t zdew(gPlWM64uh}Xy)%Ao+mB`u;)c1mx+^ksl+$ix)+%>uazKoKQsV1T$9Q?umYB}V z?cSqTKE0UYiP>>u;U!Pzk6N%j>ey1`ufr+lL6@W$1nv;g=Y!CX{Kj9ii?~&LB(^A9+iO;}u!s-kR;O3w{A(MEuDwNbM47)TlM}Dn;Gy zCFHfqEZLrb=geKpT0iLRNYu%~;c)!wZoQi|vX309E&Xm~6Ixy%^p;8|l&8n>mN=;1 zP%M%^8}QV9L2Z@zyTV8%gWjs5+pnw>>@e#%n=8<^NBy%3x>~{WQon!HsiHLa*N7P_ zSCflX$^ER!TXdvbWO0oEc~xxRRZ`hj{c4WJjn->XJ`*PwWhEHoHSZ>*Kku&4UqbC} zA-G8T5zLfl$gGx{G|v2=YJ^tqDxTDrm8abcNMrGT(MFr#qLGz3Q!;oCE~9kmUR5;Z zQk7QsWxpU|z0Jj1 z8o_sarGov+K7_kSENe`EN{!^|P9G?8iVu^^b8mR(lDb-AMrJEO9UQ-@F$hQI@R2k` z9E$S>KwHX3&Y{G9lAhqt?PKfttIhh>o3%z_2ZP9enOx#TmnQ~K#7$XBp3v|PVuyN~kjTAx)-y*|jobauGC%x%d6{$lRNVd62W8xp`ENBhZNp5? zj_C4xWYhIzu?2FI#+WA>Y|AxjYbiWalOnb4jW=+L;AYaD6fT30x~{Y)Xf1+v_t+o# zH&VLaWJ)d$607wf<5b8z`C`&>r`mUFwOQ1?N~7Pw6DC#eQ7r)@oYt7^=60Njq(z=Z%fskgA7uG`lB2QPctP%A z6n8O5WL!&)QLxXP3bWQ_in`zn}K$^JUV^eY~rGjybm|5-9-FKbL zfARkSm2SSrpBjLE+Lhso65^Y($#`?(scw$nw0u>*=WDg}WRF_6QSe(%>Y2{|FUp>{ z+#l<8X8!=ju+P4zSjWy(t$j&|&Y#S!mPszj9r`NDgv~jP74q6Ylu~tUe^U)7H9Uj% zE#j_ICC~h23l06~iGdZ_*gtI2>@cbBs#@SV+)WC#SZ%>^7h7O*^IRc>$c7FtKw3xE z$fx03B93GNnul*5@MUc?MYf>{QX7#3r$|M@zA1OW8?aMp6r&Nxkq9Eg}>RT zw|1O79UN5*By=g(;wHJpOtiM+I5J2hzZ5W_r|9h&p}MJhv_C9tyJfrd``@8NBVmTw zj*T+jN%(7DK(y1}2F@4n7ON%H%PeCb67SZje;k9-hh{4U0)3yi}Iyg;-IV^Hh znaVyYO)eI?9bzAM-_)AAxpA?S!yHXe93~Dyzk#Sw3TzCAl*{4eS5_s_(;27GwAHfU z!FGv5;F4|XN9$6ajfQn36n;@*^DEuzTvzJ`8Dgn(;a$qpCw{VjQeEUz8au#Mr%+uO zd}GnxRQ1{F3x!k56zM%8NjTrGsNzZ%_Z@8z1w-hnWCR<NyhN?kI%2wM?iM-0Ui{c`0buK%L9B50k2JLDd@@KO) zJgV>!pJbgrr8~47wzhqWk1*s$?CkL`i8#dYtZ|Xm!^ikkQqx1IFF|0HV4> zFF`iQQ`u2!31Zx0ihQzi$!NV%E@QN|TuUna$R$7OqTF$B63GjyueVQKS7>YLLil|v zcvUM;o6QzwA(lz&?1T0JiYZbHt!7_JC&X7C9&WF~wEJq+3Y1(KIw{(o$!d~tCv}R6 zey#44ju2xVuhW`} zz4nEsvJZResInu=XEsS?iX%csO7at{UDniG$*v9xwXoD1leYGM#OX`z9=x&5E+jj~?4dl3Xmn)WYH(ZZ{kW@ zR+zF0mR!w5ap_^$VX@6$pVqCKR4tL|{{R(EgA{|7igmGA&)98sdCpQVzc0|P)A&ej((4+1{{UrL;>46y zxiRhj6=pZsfnL}g>63g*CzFhe%`26M(sdTyEEBfEwOa@wxwvW4xY8!`J{kA>s?Ikv zf!b&FEkwXjczpFu%j{c0S9b&a6pCe*Hu|EE9!KSOO)Sr9WUO3>mropc+lz#%T(gq z=7UsUxc)L}-D7_rX8ltd=Tuj%+WzV=N@}h^zHSE4&L**9t?=%XY zQD@h@WB&j&9#kC){Gulx+E8_6ihm4W;NqoeT5+eFiX!?Ira1s{KQ&gibB?H&O8jfd zLs4s{yEL7_p`8D02E0gQ z*V;bG$zr$jd{z6^O!BRJd+|;HtR7KjImt24=B@YmQrXLOU1UluU2OVPsnRCN1|)k@ zOgJn^C^QaFc}H#9H{mODJ)%Eno9`gA!lSLZk2tEzWs=(^9lX2Lb7_XWB;_jgd)2jv zeGkfw5-uFV0n5|w51f*-*>`gMFyE19wjufab173Qm&;&PlXc) zcl0?%xsQ645>=lGOrMLEOHE$AJ#Ot)ZyK)3^&H(b#m{h8yWXY4qqWJEA&E>;sRkct zMO#=dmSHm$7e z#~xJEFOv}J*f-$3q#){c8<8OO)pHj9IiLv*jmnx$i z$Riool>oFZ?K(lTBiN>R{Z|UX)oZC?mv{M^52Qm3VPZnFV{O zf{s?aR~piurAqPdRBOM~oNlf6D)OqHNNMjXnegF7ynK6>VP9H#o_xV+Enzm>C?d`* zqkn>nC}jrYUd0S&NmFEc)uLk(8Ns9TIZQ)^RnN)2hUam1e7A@)?^fx0GeR*)&O(eL+sJBY}6jQI)i-B>T`4{U>kk*_ys&P8-qN`4~^Co3D z(hxG;QMcZUSlsl0F1(SM1n3r|ac5}k65zn|Z|P1R1@B@@}?UjG2;R(*6=LZEHq z$LlGVyN*ERsoH*jp+?p%N~SKq-l561#BX@HolxaELz)RARMXg_oYZ*R)%K~Is7!Yd z=3R(PIHMZ2WCnra0pv~4jcw!3g~;+|3fW~Ht)t20^{`!6+kAL|+9cP2ttV)K5+`bk zc~HE=K@{Q@a*H9yMcBP*r$|EyeM;c==#^X_lm7rxm#Yq*9kg+FszuiD=ny#K9EdY? zkhxqjq6b`W)VJR+c@-&7T~t}83RNUDt=h&Zwer#CQZG^1cXi|HR$GWMeC@_OsymV2 zYcQ&QLv6W=)$(LM$7c?K>QsM-;y$$P9>OFF-RAl;qPEJ$}Q(y7w1=t_6@vb(<5-~0LS z2i#rGoq1;Nx#!Hxd1my3>oeQ<*Wzz_us%{1bbpNRG_S%Q(=5$WNm`UH{+dPRwCbiD zTa}s3eE#ymI2?E5Ti+cvcQ`XnC1?5RPd?Bui!VATSf91jXf#G7#8 zfa-dI?`?j|Yp+8e>ZKoa%9Et#CPJZbSoW8$k-W{$BTaNl7x%b{$67`BVbuZARKh#4 zmAh=QFrTM4!bwJ%Mi7VXweqJaAySrKCXW5S%+w%4%k3L3F>ZU#<~6?RfqeH|M(dvW zNM&QYlxPJiIV}ByxO^7i@NzU^85>1q@FXgR{zjRo>OhwW@VWTOtUUSX|CYsc@d4x*&LW_Z|9xQmi9;c!^&8ftqsglQ zhW5(L(hW`Zpa8r23_QUNrF}z?;p(09l>d5mNagi~ykF)_4%0g>_KQZ=9PGlLBy*RH zZGl}x77xNDcG(+B`}r<2P}kBLM6!OBGZH;nxqJX3=@#XR*k39}3s!#rvu<{?#*Jh> zn!#;Qv=9vvQY#+=Vg9d%M}-Z{dGwAOyF7)%tzBEu=37-=jU20SRJdlbnFwfh8ep&I56w9- z*73pJgpQ@4eUFV?@yVE!ahxaVh&$BR_2`(mYUymAeq4OK$viu(6@z*`Z*ex$7wy>X zje7z=%1Lu&caqmGu80~Z>+@KvA33@y{-*m9-nj6)E$QwT%N5SXtKNc@qq9SV^Di1c zSoXW z-9kqN(3G`PJC3yEQSQsLck`TT-g+W9nmd?Z=oXVcR9j$nwNNiU!H*=#1sb>$sMMIN zZQAWJ5f1I+1v&o$_U+EtiIe9j&B>@YtQEhgyqmhR*BuV|#}HHs=1Pk(kp`E90nu*^ zG~16f(`-{&l7=MpD4Y?(U)SCPjhZ*yGDa`463PwSyF0zw(Dqg`*=vfR3&Q#Zpw7Avu4>`=&ThX`el2luck5KjhD#qn>q#^J# z>7$lmq$FE(4{cBYn$t$d(}MxF4fE6|!(TXQf`Gd=&Qy3+@|#L+xXKT2)e=l%QqL?W z$4`4~_#qmu8XF_OS)!SF6fR!_k5}b}d)1*|7?oJn&EWUYeNY3KMJe9ED&P~EfmJBPuRSvyP8Z%di*?}>%mBP7p#wp7vt{9Top?XMhfe{(Yp z>^-{+KmI0nrDE^Utr09lyBf-=h3WF~Ax`BAgqc@o8$MEXEK$FJ%0oJofOHZDXH66(^2+O#G z+zzkzUlf^Zw#M0FqS+ZaVfXj11i3PBh1Tq_+6=0cKjTlTR0(ZGW+}Zh=V!dkBKbn|9lz%)<`>(6ch);z= z3MMP@4k>)No2V>C)uN5C+h6+EEEG~Vxz_awBdx@j^3yej=!QJXiV^w&Rc=e;b<+wv zk2MAZmm$uYVd~NhFsK2_z`>j@dzATM*B-Ug*qe{~T+@*PqiXJ)pL@^i{hlJ1aa6d5L#B389Ar6WuW(p6(Y zzYWSV-~3Kf=@XPBnyt(;BlCJQ0o-`YW%5Q$VmvdmiLR)Z79R#onU!WmsW)$bfC)73 zERNb)`wnk7jg@ZxBMPf*;=Wcr&@HKS)X@2=V;)a}uDR>(FUrqr>YH6>iBdC{!cE+% z_L~QB(hj7Mml`L-@q)`shU$Y1=UhJeNrq$v=$M~(n2Ckd7MdG1vWdPK)ZtEgzur9! z@ExyZZMmq*J3y;VToJAC3H^LA{cn}N;qB9`Ixk>LZ2I!17rSLsbt}Uu^@31UtiZ-s zqZ+d5yKm419g2^8>7W8}6+CpyALS`Hvj!M(J<;S<>1%0d>%E5B6wNumDP$>9O&pHJ ztqQVU2FBFI&yxffx#~Ck=qqzd*qQQgkN0>xRTIZ*P{b`pu>&x?`}hN)qtqSQo+1<`qE&u)QI_9Z^E#I0_s?vS!AM zMjfx*9OHg4dGmg)a+;+ik(rV!ML7CmW*}rHpB1y-hMyU|;R#GR7KAhr| zW}u4y1m}ohH%iF5EhU9i3rqQEzTw_yi!INlhH9RHqs6>8Jknl+p53kACr1eK0g;v^qW?5GtP~N2q%?pOMRjjcy>+3J@bh2S z*}%emwR`P(W>{)nee7QOW|rUDBjOA*$a{ST-Q(BD8?c!rLKJ;WQ0HxJ=r^9fsX{OYe)V_ROP>w9-SbO@b#o9tHK{qUfi8 z>9%_BNywd5aa({^sIxS+p7cca7-_s#f_@}8b<}FQmsfvE4D=#e5?LQ7W`^hgd3oT0 z3YnuC$zp8Pz6>HFua|@Xed@{@gq`&end2EmuPNfjiRIq2x_ceMg6c-q;zl?*e>I-p z)*0%Q^{c}Fq6AeJeE9sV@fD2IM{p+54K>0gx+HeFzd zNoiL4Tt#_jFXIR5cc&=VB#|uw??cA*&4&T z8a@7V!a&u$uDBN-tvCyRh|Hbg2>Dc2fNI|T7#Wi`QUDy4)!Tg2ZeL0lSw|SHbs{HQc zO6#PwwVk{{@g7(9rNPd+ygk5_z)NpmHBR>~fPLjUaq`Zg#n&8OYTi5ufe4GJ{K)9@ z-Kv=m@X|#uZPWsZ%5m5#=nVIxCR<12uQp`7Gmrmvp&gQU^s=qATN7KusS6fFGMJKd zNrxvr$8T6OPjYppFSpZhuL-Hl8KvmiiE{hi@@i_(qyJG>Kv*s-v01 z_6|Jr^~)*uAn%*BS`s$Hz*<&BdV;z!E&qJ&rxyLj-0t8s5{{Z!sYOe=_whNsrQ5m; zIzmJ_N^dl6$`3isz3>CPHI!nOB>4=y3q8?mxeZE$sFKgucMWSIp2644SVdnYe$-o? zVSoFP*xT>TEJh8ve!rCqFfU7wjAWPhtGZ>&r>8X&(xXl~b4GvHys^az@I=UziaimQ zHDi6X*^_Ct`kucKtbeL2WpK86thZfY-}*vqIm=R$!LSu&oyY4hO2H=LW%XwlkpA@< zkVOP>XM<)HX5*gGJJ*vKG;+`}ZXL_de^GIlz4JO&n6&gARMF`(dK);dvNWHN_cG4z zCp-6rN0En88j;zvb2Fdvv#JVyC9#D(t-5r!14YAJ{Ed>eB7cr*x`7V`3Jdp$Bim9; zQA!$iSQkRn3t;#_TdWm2H^O3XaNkzyO&$Q3>ZDJ<=A+{4aND{5(zu((sV+AKKEhSezv z!CU^c&_|AOnx|ts&ooT(H>WLFtG=dRZze88P+q~D? zd|CV8#-RYc*E1Yqk_cd?cW?Nq84@0n!;x9q>oPU=IU4Qhuy;JbmP|l&zi2*fPTZ#oY*c0arsNu3+Y%n%Iyc-m z{dBj$6Kv(5*^H`d~pAZ0+WFLAT3Fe9HJAgKge|q4<-~08q8n`pkl;srR#&SN5 z7R0ZY^S1g`D^r3hB0?Sw(1TXcl!ig_HWHRZ+N09eRDyn>IG>`LkBe+>CCc^_yB; z3V-I0#oRm3Id`%hzxSK%ni-U@p2qUnM2l84D1Go zn=ot{Or*YeV_s(k1_eqd^H^B;Ac?HsdJs39ocJk5fMfrnOs6OOAPcDNR4Tk>Kf{&H zf0f@yl8f7GVGdJy?$_1%%{l5(gYq#|Uja!)+J-n#j5eK9NN?XpL5Qw-~6HMzZq*TePtX0v#9H;F{la+pVz42v}5-m)}O+Tb=wu!e0>&x zUDQUVR3V-ruvXDs>tj3@Um{i9)D_+4vBu^0sCKQzgjR}oW1Qs!ZvW)^b! z$1!rIU7O}_bn@Pj>2sSGd_uB%WtSw(woxrZn8NS0^*?5m&$IZn+AWCKR}63Y ze#+dw`n!(u;@LA444LQS>K>0lVe*jUl#4NF#+UJ&9oor_aNg>j^LxluNb|}Cyuh}4 z_ZXgBl2Iid-s%H9n;qie%|1xQ2goC}%)CF+aG08Zfwc2i0#Y;gFu5d5+_rk>@G&Ty zu}VD5vgA-aOasiSHNAkKeE=?D!oo^&IZ>3?y(@`OC_ryWJGpR) zR0QIe!N9d`KxkID46k~4%2khT^_BSc^j{QDA9xfc?-ig*e>ivZ|FrmW2yZCK0kqRB znL30g|5JME8mXjp6AB1E3H_hc8P!j$T6Z+SeRmHZ!-o+C)jPntNDXrJPd#2O#1bH& zOvcYp=t19K6xL|~!7Bh-jRhLxW566*7f2(m8{i`F1AzPwzDH_6@hL#@e;+DoO(JPe zrDW!B#^{#{BgnLb1&02j`A{{1oWZ?J#*O#jb8{s$er z=>M^@0KnAR0VHraME;Z3wi;$xGI{>b8^JJlz#4$;fHcTi04t=G)}8eKi**GwCqGAi zZUk&49{%Mp1y}>f?IITdjXfq;5TaU$(}lk%9|4%050O6>5WN2^3xHE~i2Q%Qg6AD2 z7s%7}N;q192^|ELK`JjO;9NAUpZ8`jsDHTHXA%1l=wcnoX|vibpljR;v`KV~`p^vA zFCaRrp8#(_0OI^bQ93ja3q;p>DzlBDPL?y!+pRMqzlYMq^ffYiwMD|Fdhv(>H*IVTJF?QQtY3~dm8G*xr@5r z-MX)}#%Vq|CT3;uXn3P<)~<`tynVXbaDM!UCKMPe$oDUbJm86dgWb`9CxHXgJF|q? zUz>$orEONZ7PUJOCTRRqzgzTuRBl&0`HpE;I#zGo<%#^`qjFph>;p*+PB#KGR{rU< z3k;>O@F=f?s)t5;EhUo*CT)J$4i^OWOlv*iXRnviGZ(QLzS^(V#|K#{U2{xP?9P-q z$7Kq$@vU`58Cfl@2HFTG2-fCt!xVFm;EIwCfu=Su`At~U1&~W^!Hln$^pu&aL2(~q z3=SEZdP)GlC)L`W!v7SqOjjg&*8%rB=;^I;)iuks5EjVp{d6!n^4hXlbIYn_L znu1>7F9^%+TJMOQ4m`Ui1;pY|eoi=M@LWYqt-I5VimAggxL9$yNK4)t@D@Zu=)Y%Q zYg*OqOtoM1-fQ44*m{RRJw#SlXZ?~nx*O9YnG@zct*N;JN&F89w^MSvszHxgZgZAd zogB_Fyo-)3xg0MYm_NQra)KDOs4exJ&S3VhzIpWhH=BuvH{FauPlV?Bnm+4|*azjk zcN0(b(d;6)FeMtuRil3Eb8kmubR~f_eiQk-9(|^eRwfvq5cb3UBky(6ISu$EG<;2I z8X+a&k8^zA_+b6b%%t%eil6Q(bUWrWO!c-*$KAy;$hk;1@;qr+iHf2^_`Xv1d4}1 zPo5r;N*QwtW$QiM@h2IrosjT_;fvb8C=(AZjUPJ--$e}wPL%C?WltPM`MEI~Lj43X zT%3{?_MNOA@s|`W9h1}#!F5I&A}NhoG4yj|#;BI>1Y&f;`oLlIAdBIKC#MZpFSX3Q zs@_pU*{s(OLGE0B{sCjK&psa0;r!t@0%o_b&cq5ncAeVBiy5lqmCtbEq-8y0kp6-z zxpRhclyL37rosh-s~=V?eCxx7A`%Pn3V(?L@7I4*+u=12P7I4Xk?VBx`~pMQQ`|`5 z2H&qRkuka1MneyE;lQ9J$dz6_^L)rub!0myy;AjNW@vQh(g{3nWUa&6aA8ULnUl~( z@*u}53dXRj(4(8{O~$ABt8!wSibx|52^@W;LlTDp5y{DP3`b*UrDtQ+?L9fq@7BV| zOb(B1(ivQ-#1`O*P!97c{c7jCrb-{B7-9E!Mtb!@>@p&wXh}x7Q-_8T^UhV?`aLn0 zw!iOSTU15+0Utm$&&Aft^PYJNjj6VJT#spcPpy#{{w%rqaQ zEk>;08|#xThN{qpUoqmIt%!#YKJ?c^F2rJ5xf|+A^9v+2ymh|cT=jtQg^qMZdf8z% z9EnpM4FhEijLAV$F`zsYu0ag9yY6qyU2j? zNk6N5eP~as=p%H}DDC2QV}XPyAdRQoUfVs%jT607F&lzd!GS{~HivN*$yo~BwoPPD z{GeJ!$5nirwzCg2WsMc(6JFwNpW3N>%E&D=fEZb?ymRLR5CA`aF=AsW0*LN81?ZRcnEqjel|$$_830??`6p7T$B2?%^Q0)D(-kz zCT7!s!ku7p1lumDlLgxN5dI~p{fpv6W=mhbnp3uB6!^g=!7>w7Af-Tsab3qPV;eS1 zoJgyGe8~M)fNUsua0HhqC_QJOpx8xoZ z^(#u>ApmtFRUL0Jy{hv&y#erTHW>Nr0YdaK@9-hgi>c=&ZiRG44ZK;=%&;AuX{pzo z_6}=mnBTP46ht2>i_v9(jn=ty7W(dSdu}Y%Nyb&q z#R_rsh*XdIi!#3Z7iBOv7qS69h}?S0x=843k8{VpJ@w^;<(9h;J{|=xckbH~!yOr; zzV$F%takL9f}grpqpn2Zd2ZBl`fWoz{aLpY8#EkorEI?UX=KH_^R;IsJa74V zkt!?ng{u0;_9w$Twf`X73Nqqkw;7FA#q`v0^-db|RjWJ%P=Y(#kEkDb(F&9fa2WzQ zm6Q9WTdO<2m^WL=Y-VQ1<=b&O_4Wfz1~G8R?P0=8qJmZI>(Eu`_hAI{hatYOJCy4wYPCs{L^kl!NSa?cTM6pn!G_btX@PqI$~TX!(3pwMH2g{;O;66id*+??32 z@(UTmT8`lDqSD{}zRJLs2OqcR#mTG#nT+-Ldd{bbE_y@1slaUCdG%)a{j4!rAKF1+ zJj{ZM(wNl8;~8z8OaffGXyp^feD*fm1nXgL42AOAsdJX9aUh7krq9LAYO;IZ>TFBx z!RFixVxL(ovKv2NQP%nhTC3e;$qQM^H9^&8jHgQtE-hkN*L($nZ(=^W4G=d3mrh#( zWeF|%r)YUv<9^Yl`_R>ypTiZr75NX5uo8XmD~OeQFDv-ehivLIrV%1g%fZbs{@mj_ zd}nUaKu=#kejWTGL96P(r`1{Xw1Y9ZgsCAd_qDcxVvC|rcI&I7LJhGmwbSd*Bg$&q zA*PsUKzZFH>x32U5#JXk0nQvIx10V;m15N~@$OB6KIPvv1lfqy;_S&P4!aWLBQ-$~ z?FCX-nyhji#<5a;(+uOG1xW|&-g8tY!f#71#_^nrRo?Zn#-lA>nt?oGaN6{^4ysk` z(VpG^MJfMNZ~rEL$MaLUKSD^>}%x%xxQ^ZTP;@&UDL?XnZoh4#Pq73Qq&jj6>m@j*VvK?hEl$zL@33XxGpW z^4BAU*@~y-N+QtBcy9W)I8#D&W9H|E1GjhM6<6OFM;Y`7%akE%5uoz{qB$`gHj1>d znz5;%5VzQE=b%?642=g+xBL6%!X&AW>}HMHrrUT?2K@~syk}(7ab_7(NHfCEomzMb z$>SUFxczFJEAE8&1Bcivhcy6;%uSK7lcc-I-1Teht z4V2vYedc-h;{z;HxZ(@DRCX>&a)EWR#|olAs4`NuI>{!Y?2|rLK>8t7@w-%ar5~7* z0Y8@z<=XC7yu`xhPkxBTaiWBAkK3iKwQuXcfBaVZg^X_%PA9ENj;9eEr9}55MRkE+ ztb2b60Kf(-d3Dd19C;bZy2a2S{OKVk3hS z9lTK)CO1RG?9yVsAH8T2tKi((+S-3?m%2RU-^?>1$yz4PiCI9FxD;EK)OQF5+UJWc zL=hPC<;0wFplezs6D%+0wf#AV&5d{+QiujQf9%vjHi1XNd6|5Uw|6abkgnl=>_1+k zw5%{Xzps2`hdgYt7}>S!^VXStp=k=l-~FX2MMpy?q;iP%>Kik}FD5K3Uz$|nG+uxi zAMLvn)p$)c;W8Za{2Hm@rzM%njHi|tX4j-Rr}~0Yu)k+*zzWBnxV3ds|Kn{r&z0)V z$otgj=H1il@d|x@9lePwLq)hzfzq`yqTz$+`&i9 za6Be6GYy{{(n8ca9Z!euf~#A``t z{aC6%0h06z+X__S6?Dgz`(0L5G~_~P@9}+jis}betEUh5brhYI=;KuOt;5{G*Mnnj zNxOENty)9sU9rrE9|RHm9xhtNQk2jtua~0f ze^I_SFb#artP-rKvoJ4!WV!hv>o2$1b_o{xh_++#iUz~RL+Bj;v*ft;O6%WTUl}py zYZZ>QW<*<92+Iw1jiP&b^~w|jvbD}-Qsn{&3@crus;L1==A=g zydHD+fgoG*gq^uVxshRT>EC;+WxFYUHo}A-C;Z5=x7MBD=P+~LE8<2yAppBf}`X8ibtqOd$Zoc^7pS?T%t(;N4ia_Lc?;;^>j1hX{+IjCX>LP zFu~DMa2&P6VQ|X4&nVj7iS+cU0x?kA6rV!B7gXGFzBu=@vD_ew1}k0MQYP^a6I=+p zc0;PsW1K3_E93ooh)&|U`#>Xw+1%>7CjA`iI!+p22gFgVKqNn7P)bAbX2?>c2wmzJ zuj^e+u^lhDFW+D*TW5*b*av1wE%Deagz2#e0c!sMR@jN&aZ)+|gue5`kk_Fv-e+r* zgU5Y6chXnJ;PIQ>Rt?6HC5$GJjY0W9YB+tmiI`Vt5r1$|a_(BwqRayQK;h_Ma(G+bDE&b7JpL0*)~n8rIOuVcEwefmzDjo#gK`m40-m z@j?@DTvx&cFP7{waxL7(Cdx)v`?Mb4siK?o&t@sSm`=FZO(s{!ah~o}x^$6hB@?<5_kGvF^zDek zTnVzd(wCj*zw8X8v5X||^Ahu0wrQfuv0Tb8HXYDLbq&1tXtSR9+j6h9qy|K3{V0^y zbkv%!P|UaIUmg}D5Zy*9?`SMN>Q!$ic5JWSbfoXApcZi`O_Q(d=>`q*YC=gH;yD!~UW?iRPSAy<5+@NmW`v&TEH< z5jkNIisjRL@Cc^-dSs??4EH0RG>bv?l=*GL*)HhpNOWQnxwlhM^N?VB-lEsAt!cVK?%pc@IBj{Km?e8w!z!53Lv%P2mygz7hfAo z)1r8C$G#|T6KWXoUo^ImY=8Y#w`C{ zhGLs+*Qp<#^rdfvwEbRwu%X)K^9#jhCEiXcid3FFc|76Q6Uh$P%}isQK}+_iTYGRz z@SK0fynF!`mGwt1vkMEQAA(+-vM{)yT1R9<9OIJ{w&dk+<)sHPJ= zDGIl);V~@Kp-PvuzY=JNQNuwZL+&!m&WY5gE<=+s`Jj`7fPih|9c~N2Mg)@%XGMX1y&hMiNdWgEB~rGYuUlkyZ@kX)ZguqrUgSyH8Z1NQJLV`HH4zV9ggk6F zh;QXrIklR+il9(hqxEaaH%vgdI#wN6LsoTA%nNTeYwruiPAr$P!dYd+K zYiEf_=4vUGmTU0ZPyUON(YSLH_Wn)0{@WLn9ha~BrCAAeCcZI)Ku+%y;)T_wVqJUs z2Jf|IY*-iGGlabtJ*KrR4$#}{I{uFr_-_j^tGVxZ2Y)O31L}aF7pId68}lc(;MP(D zT3y*;B`TYf0*j&Zc5t%Tn*z`8JtG$Qd9S^;t5Hrq|4sy2PWjoO+_7}i{CuGZdBrPu zGve(MMy-L>Q)FmSU2VHq+-LbMsCe(;=98hWlA!id=BGvWuor(8@GRclEWviq9o8RKjA8_Kx(jKMmBF_(kF{nn?*C7=y3U3Ib-he*7&4c*SzS-c9dOQ!Lf9~r4cQP{ymg0oF2}UO|DxiDy;U4z^-aGLF680Mx5upyJT58!%*?x z<+S@z)oRTQV@$x8gPNtAy9sQ1S@Z))WpgNW8%xI*m zJF4(slrX{kmI&T51H)DtC&t6X-{}GY-n%DNQVmx^>V&}|PupSTu4+6@`88E?b>%*g z;{gskcdD1A7GfD@US4poH%dmhF2tgkg*Ijf;-CiE(1ikmG4b*D!xLG!9wW95GRnE> zOlj210w&M)Pk-kFgnzBx&lviy*HiTB69yKSwtG!ay-|qj$Tb z#$dO*H+#HITi2e`{sbeb34UqNF} zXBz}m7-ko3Ih%mBrrA4Dck9PL2Yj=7V#<>lzbx(!!ul_o8>}f<{>y(i^~J3I>uMy( zSy<5YoamE#otqKnjLmarv9AC{q&Fnb&b|MR5%-6?#t&`rL_IL!joe1Y2{NfeEbmS- z-|Yf|k_>*9zzChNA!p?(AMN2lV=jtY#ZkN}!X{@RyUE%_UG#{cjpaG>ef^rY^5U-f zSthXMY4$qpZ2#FCet#LNak&R9a7UwTd~P>dZB&bR0tE87D<48hK?|4HBhzL>)te1n zQ_zCVXb+;^ryk>`$J01TVbvUK`vuRSO`&$M8A03;j_09>mA~PyX0EjUQ*w?$I3I1I zUXH!I-BGYjs?cmL<+JJ>O*ES?EhGOCcXGNV)6=!Vx(WiXDMMHNyHdy9ls`E`NgbWh zVNM$UgLjIX{gCiSbu|?u)RAfqQ#LyXFl_=>`5U#Qc7>CupIedWvmlTjccmM%<66Gi z;nKO;FSA$00)%S12U#ykRJhd>w$REx){|>Lt2lZ{i``3ajB9nd4H5L!hP+B63-Hbz zC6a54TYH!F3XXxTWt98fqf1b_q?f2(3~M_b64Qk6NEYNc_=}>Vroc4!B(g}F146BD zrr1j#XS7dXHrJvSIWzy#@eAd|i|}RD5|?1@sd`!DK`xeVXk!wQuzJTrfo z5lcwcp1h{k@T%_r8Tw!^tXil*p+38rq(>8nJfjZHqH@B7qrt#hD*u7 zLUV3W%|}2eL|EJ^OBS4SJ}P;Y-n3vR&6ySYq0ZTlj=B+s4akGj!edpmN6<%6Zrj>c zDA7=d*L)`>o^*E8uo;aBlC85hTP+Bq=Kl0?^&SO!F{<5vEBIPE;BzQFCa=2A4|<-t zo>Y{9FW?;;)A7WdkXdVoC0h^lXNaTKteaK&yanZ6k?rfY<6+d_h>t)7`0?=K%q#ER z>&fvO=N^fdKEdT-V96la>_jcOLOJ1~Bn1w!mn*ROKQ;H}4Gb}FWt|=FG?Hg>MnpJV zk0|rbPnG+ln-y5uIW5%Pd(L=?`eFW~pU3YL=^wTPUeXOik7GI&X1PY5)dcmXq}bkX zZGg1;uWDt#_`qf6=FCbfA11pXrh|DBgC-3uHMj$J{{0yo7S9 zsh4)znsHkKZdEHCTRT4PcO!X)-Lu^9QuQmWJ`4+d)1kY|TWF{=tL%d}YtHdv{l}84 zl4Eil`eTLMd1;nfd{#28Tzx}`i+cm!7~3+z(IO7AiJcp|?f#Ux2#aa8OA~1&e!l;M zyrv4Inr~MTV;bkUx4lr+@d;@5-8HIl_N;4E*h0DDYeI6mMvg}95=XX!o;~Efm!6(z zS#g}sSKRkA2)_2qu4!je;17^kOagACYC6VA$pEZ(_WFy4x}tE-Rv_unK@`BRgjv41`jF5|>o$S}RtLri_&rOtCPhrBLN{n_XI~t9Ed6A2(tyFH2!-E zkuOZ2@<_hyFAAskpV>0Odd&vT!z7eF#Nf7QF-DGT{m;l^O$L$p0Fo_;8PT58&AP0C zeg3pgb@QW5Ajlrzf*?`O>QB{#X$^aT2@K?k`bfpqo0X|bWbC%Z4HMH00rK~C4PzjR zQ4nkK&UJ&+RDnwzPDT~r+EK=#zcd<j1ZhDEjSXAE{iSzLp)% zqRz9b?ukNaXv7~6mG3Cha8=;cn-J@p%Yg4s+j%LhV;!n)f2-%cFq~g5;dv`iQ`>!haV3@`uOt*AKa7tG@;`u5xfn;VR_2tv z9;JIfFT72=cUMImDui)Ew!#{ppF{l?RM>+o#kqHysauV$n<+~c{4zm!? zU0;_b%NBzezgJ?1hBoP66=x46oQEbMySQ0W&FajZ$UfvP(``gNM1)g?H;y7`_(VsX zooz@48oVN(Pt+<*)9?ZhzHLmRi#U7Cmp&~&Luw&-Fytl7C6kWwN7!d_yL^^(M;DMi zlTc4go*j6+gDo%l*ld`hupTJ-mUv|XZa!;tFEV>Q!FILem*a0LCO-h>jp41tN@^S; zLn6(6gWJ?uzmjswGENAc&X_0cdmUDhCX znCD_MV=;@4>uR0_ZfQ?Sa11GCjGaZh#SAs-c(#7w&-OCk@s=OAU={Ynkn-OQ~HZI`66W%gXY&kHt;EQ->h?fp~z>L81KmoOSkDI5`3gPk#XXow8xTX1v{O4OIK)KY5GI?LXWvlFBtbcl!^+;70Rhc?9WJI*(tUYt8iv zS_h5e+2!1Pr-UD~zmv-jd&E_@a1G5to^U#g zVrol`aX@XqG7|JAN4-$$S??4}ExPY-rcJ$;QP&e+b-D}7K8}xb-d(Z(!7Y2#OKe$xGE@K(wL`>O*4!G9PkG*6tK z{>h*vE?jdtC-dD~$&vCQyUs1{3gq0^1^TLMG$m6J(BVt8Ev2Mh|uGxo8JdqLHR%B+2yJWEl3P+$rjge=3Z3oU>`B0;?laieM zm^~9`WqFaRM*B887lb-RU0%q$PrO{7X_}(%>$)7Z$LC_zIAOG&lTIC57q{Q{V{Qcb zG0)lBg%ut{ebCS$MAyke2{?+_O0=(^_zQy=+VnK{TA7~|I|N{VOsMrVQq+>I-GSMi z>TNt}Y<-==YCksE|sTz`qDo+5YMMCmDG7q;4v?)PV?X|zGl*Wh?%DP@@~S!|!6 zV&c+^Crz>NQcTo=bS>Dbfvqw>po{pKy^o4*;GqfC$_@Da59}-! z&o$mPgRuRWV(wC0_4Il4bTa3Tg)S@g*wY;-k5D9BmCi$SC6lc!cs7;yx2G31tb;?R zgsb)=he5pM0+t8lNAKZl0RyY5yEFFBB7%X<5t zC)L#3LE2a#D;guz_KZ0X#kLK5VZA91^4_(@~b|A-bG$OYSQl@REDt!Bo^Zc8Oyt@O^{RuAC-z3NOtU7&V^N?z4yUq-38=_|wZ8H+i zCwshdJ`_0@o1^Az2qXlx#fyvY!zJ~UG0I0{tdAa|itJ?lEDLVI8zRC7>JA>5{03WW zo7c{}q+t1EqV;#g{GQeGQU&MXr9-2J>BK?VaTO+!pLV$Nxq+04raRfEBfmAfM8x{F~Ichxcu;e=G-|4uNV?H;9L z`WR3Q8od}IYH7jVkzE;nT;7yZJ#8mBO zLcnLEp%UX<;@n38$-)*Nd~p}5wR*|>g?tXVHPpxXoU@sQ(9%_~Inx{l8iA9MlDeS7 z&Hc#z{^3{K5m&?6w#bIE{XbDp&DiRwhwrPTNIx9;?~{eSc;<@I>Ug zLTsA7rtCv;L*3B02Ge#tRS}0c8|kPkAxx?uj*`FpYqHqYBMVoV_zvrPpiv`yOfcSc zD7&23VS2kJIlGyus_4J^sDhaJM`#%B3IXKyGm<_r@%&V?u>MPl4r|DV%s!<^cIWDH zhds6)hclGlzru3!nXx9?)k&`EwMefe z%=F1>Vta`{`AHT*U>0G;QKsL)moIu5xqp0MqlG+>(!;VIP)R&xc-AV3;Y z*&%{t9|U4QKHd(g>17Yq^NMr{wAZBDEVCL5fvLYr!-Ie-$~3))!pJ7_hgbgx!9YI0 zttI0t5>YqRVC3vRLliWzRJCkeGS4$owHa+t$+nZf*AaIZ$+)^*8C)6v-tdjKvwsqAj9hV{5waxd!7If=?A}2Dws}CIUHtlTebx06*O~ z--;+=s-7t6YhDYqOPbJdS)Cy7fnXA{9s0_D?4mHILhd9 ziMTZW5-GUhl+-qx##E3^#gHGKI5&ECktyp|DE(tasjqGS0Qr{sVw#xA!Z6aUFBnD^ zNjMQ$9=*bL#C0=jYcm?2GpTmC-u?c#fyGNSFr6k9=3{0X0(t?y*qPd-RE{R7gz*Jv zx8+;!dvw2CRmmu%5;buB9Ga=)SBR$PubwS5G{#bd$`N)dR>6aJ9mmfa+-U{uKDd5s zi(1F$f8a5-x4pTy#H++aF-jZ~7gC)f_9x|t;*KsT)73U%Z>A+^;XD_0MncxoI=0sM z;COiI1fY#1wd^i0*Wr94&Be2aQEN8(&Wry5S99^hjrAw&pWkd~W1*FA&42I2@-PUE z5FjzVxsQBQI`FTjSY^8d)%~xjz;&o-q`s0s{TcXS14%5QIZc2?e?9eM)y!i-b-M_Q z{oHIU$n=x1beXirC&2B7N{M3(<5vj@(w6;S!x~n^CcyY)WB1z{n_d19TLmAAi)}+~ z3)FltRLi94)(ah19nZ6kFvrEPyKs;aLHP_Aw+%yi3&T zJ@FM(j~W0J-bvQa2$=6Tn{cy40nDzKzESDknH#%*GR5w6(CADLgPE*lKb7ENa zZEgOakuQlpf5!yzBvWHy>-YZv?5&1V#0=2XN4~vh)oT8iyNF8}1DOVo(u?jdz5f8s z@lhE`DlD@v3sk|IOF-Is-|og!J!nFb&{8`rqgV`jY*?PGJ9p}FMNdf`nx;u9UxxUI#I&HD-m74Xy@_`yG#bK^+A*4>ijxkQ74K7v)47F+G^{{Wj|3Du{JMzy&BZa-Lg`Qk30r>8vJsW6Zg&_(aV^zuyn7f4Pb$sv-OT%27d>>uE;lUmCX}$Tj#5@h< z;&Le(wS6$&zB|aFTZA}5{?46V@5c~DPCa}2tqS)#et+ktD9q*JcVhs8O|3L{xc7v_Z~C~3dRmF8r%4}&G)F{{-B@gT@2l~}4NXHl{L3Vb zB-W7Gl)c{Qk~q|dc_wL?Sy@;0cIm_&Jn>rtH9m+b+4aJ@ zS=voe_dgH#u}vT#o-CJNFJ%7!99(%}mSJG@_Ey^KxZGdlaT=&C`rS9js%;>~3FhhW zJ7TZxihRbUivdnD`Dx%CWY?_7t?>3nGR&H6w>zY2`s9WRueM>IfYP~+ za=X|m3Zqr@_4ixu$D_^#+wBsec)TrFM;(5+^p`53lAR1Wom7k}Sn$)=-s`trfWMY1 zCt8fXWR2|VlCV-h2W`BL6FwD@inU=+G-#!yhl}iyEsFar51t~*B(8&nQql2kMv}-9 zP8uEFKDGq^084*{IPm6qgpyRPJ4z~EBOHP~S_cbAuo_m@>57WVjv1Ot=^2Dkh@%&< zHUs5_KO1$%Xb4OAFyWr$$7!RvTX(VV=Y_|l4;i~bo5L#Y+?sEJ{l^uQt`}`ucGJT; zxrrRN7VWoONz0}|#Zrof!((w^j(-dtQN23}*O4IM!ouFuU*7WD8L&nNT_&jm@@>Rl zX4@05D@{tiv$c<_zj27D=dP`hONa2nK(ws`T0N76I4jKxZuehGe;dwwY<%sS&sW{zt;wy ziaN!bJhC4XB~?I_Fw^S58byd5cRhLxdWe^gz%Q}Ve?|?C;^AW4n-c})n z{{Y7LqdaY3)vTAkhq>FW{{W6MbZV|)l8yjti;EXpFBr;7S8|v#| z2iF$VHS9iuBNc8h4C#|^g~wb{OC;$Gbu|Kwi#j-j z6Qs!Ndw@FqF~f|}6+&4|b+qcP0Yl_Q`f!lExqUNm^;)K(RV2ELfZQ&lxa)~HnT}2i zCfqsr%zeLfMi0G;nyKnzY3f^t6D*4K(-2c#@6c#_?TMv=zb^2)7$l;xP`Un#8;_PF zH2K_0?G~wdU4lNQw0xnwgKh_2_^sEQhL$zFl+n7utzClkdtJG@4w%y%^5`Q{@oOWe zatR<>>NoGT#{F@Z%~KKze8W_<4wHD7k~P!J)*enU(Xuz`4&c~*#c+|Py*szJVx*JP z0!|b%l7F32!+b00wzqwg*%-Em`FF=DXsj#=ZXN!()LpNqMTJLvPV+v*C<9Y6-^*-S z-Lx6MTK(`sUDh+T#h9E^RHm*gdbvXpWw*#+#}Q^cG65sLA4171xVh2H;@$B)o0mK9 z^B5vHS=B*lETHM!@6+LhKFMvu*lIl3adUma<=kB3Xv zrll;vTYk5~zZqhE`3rzQ90rF@u8aieNx@DGjAUPTNh9t!ADc`*YlJgK(Tm#dEr#56 z^Z1-XHOK0VqetfBPNoE07Wkf+geu0$U4vhSSDG0we|NWhN2I2umf>56x8aJ#I&=e2 z3aE_rgI7xr_jt=QSr$g4k0KQ^M-p0HF$nr9S#P&MN7sB&(&bX-Icue2Jf=wJiYbJ0 zaEh=Fdb6+7ewgtYoOIQc@<+ijPXU^vod%;Dx9HBRevaD%-yVSQ%yjWfJ$(&DNl^~9 zV6vc;-+jk_FP5p8R(-2p_Z#9)B#AIejmCs= z-+el|?blD&8Er=zBq<=e3rA~6!)rd>zfy8nIRkk4FG)#txhCFVo*U;RW0GANe ztg%5=oaK*Q9O9#i1vGl#OOOx{+NVRyeS>1A3W*`8FjP$g&8=jnlG>M0W@{dpog|iZEy4 z%O#`re67*VtT+8(ihs-&U36=ty#}vwdBGH${{U&P`p2#wlSqm>ihSChq||dv9Y$prZT*uhOVZrCP=Dy)j>p;miP91VzM~kNyE~`#EicW_51CLnu?6BDIF z^ubO({zHn0QDvHzp0-J~$dR`VrV*&&l5b){-=_GEXkncpp`fLqRH}4oy+lbLHl4d; zIcSvx8R3a4Wzv-{s}f9P0UFylpQvGLA3RfL6J|N2)s%b`ibG72Kd42GPNtCW$PN~g zvJCMjo#T1DEHu>cNc7vL!snXX`Qz;jwKE8ork$zYD)Oyv0TBX?)Za;Cx4np^mABv# zBfxph(!hUy7G-m!#0Yg9+qM4y&iKpOH(^x?Zrf|~{{S3i)s4WHGN7V!MuI@RCcBcv zU)=#YBEukYJiQ)S+Cm1?!=L16ed|sJ*I-j zHigck)b1=Tg^WgG<w11mQ>_Ir`4^l ze~uvh&W>osm)28&^zG)wcW(Qe;-8B_N)R!ZRB-AgzhskdJ@F8(qKRqehC>s{ER88+ z8)(#Zb~e}ycpS=tT*@jy@Y6lQgmwyy_S|$qhZL_WysQK(GTjpXY#Zf@s#^5f>OECT z0@2B&57D3N3{!}dO{S_j7zY}Fw|~DDm0CBCkgE-iy919zX{pva&IJiW5T4AJNObT0 zTxvW@Xq}3JdE;ID`t85nj(U@zk5?il$LM}H7WiP4k~TpZl?!qfx4UoF>9#m(+9X*t z@MgMdxZ4|f=&@ng6W0>nGK~=fgm!a#@AzR@LginhZKrOyw2*$49nhCjIYB#PSVjbH z7?n7`hK#nLH5nCk+#D=2DCOJ@TP`&45=(PkDu(|6OJRLoO1J8ygUa~iSpn6jSY3L) z8)8VFzF4(_j8+2I_;ti_u5$>oX-+j8Ev`?!Y!r^Vi1)s&4fm1o#DOD%s;5w7k|~!` z4Y$Vzu4v<#S4oMb9R|au3$n(MMof)1uoBi=Z{_9l#}-PYij;Mp+(j1aw_GS{h?<@? z92t=>3!Sgl-{E{XHMGJ#N@d_1vIB40_~5iMM&LG=1YCTN-+$Bwm9OVN{LZ8oDpszA3OB=Vx_ELQo&n`Ad>s;wgTe{DXZd$$m$P@ zjH3X3<{rAVKMQt`D@{jzO4ABi2)7igz5Q0c1~B{bfP!#SQ-YMF&QO)l;>C0gA#7wz}rR?w93rAP}0u#o=7p1p61 z^mGG+|va9>^AqmPTjE-+_!n01)$svliF>rsIlU2O&oY66q zh#c@*I8L5vHZ~{O(0==2VI_c2Y@}`*{&>sYU_EY9jN?#JSa93V&kl?gcaa>FvV*07 z+iYec;wX+p{u-V$twSSH-+SMh?AOIgG|Ywr*%e9N*yff)@z@*-vskr@9qc^tk{K;~ ziy!&mTlH;;E=NtT)A7V!#?RL2jZeYseRznkf7ja?^bzn4vo~MO+7i3L9sdAE>@kwb z{_`fMC+drjPiS@X!RZY^U3MESR43=Qj^22uo!CgN6Gl*TFlM;?Ho!)aY27bSm-y^`b?F#DwKG)IOg~OABZ9(#0?Wa3eZq_*+IN3z}D|K}*MW<%B0VUCCaY z*zlZpfPytr^~Am7P5NS0FB-#*>9}2+WB@H-@Y>nA4qF6 zYGil;RDpfO~+C>tbfgviWkD zlqV7=sA}_3AY#T%ZMf_$@Wp?uWin;*1{yD`p(F6dMM)lGPa{*t=0%c!3oR9-eG#tQ z-AiBNY&+J?MKeZyWP>q@jBM5lUzmKcOIDFHYNA;0;ZTPhG0X^x?bEKK^Tkbc>WRXr ztBLtc905EsqwZ`wf(OGHK~t{OG)0H%+{kQxg>jhEfua7*2sB%ehOr)9PCX)V6ReWb zM;sg!Y1yqC>fA+b_x)>pF_Op@+*)1^`|5rnKTl|4roS@q+UfHLN>`Bs&7{K#vd7%x zF7%oS<1}^i@FR>3Xm%J7X|`OR=M!;FDB5 z6LplYPrC-`9KI=dE5wZYyTsz?e^_w(^u$~;EH=K26Tg2vQ_BRx60=>)^tF$c{{T!- zk?WZZ@HCNAJaOtWYfuYrc5a^xE$kjKqDbGa!pCo>B6>o)g&!K)^l1QwKj!_gwQC(z zNQ{SxPyV$vk1E6aO~1$~IF~WZ5-Pf{Vk$%pB{eS0Qa`1Mx4@HpRq^TsRWCFQ2D>~6 zsZiQ%J)3g!jkoEE=BCU;pmR@Bv2v!(V$tuly{mb2#XS{8Rb@=|F}p`li(^a>YUh52 z{j~yqIPg`(%$#9L!Qkd6-ctJ4eurV-z9}kbtEHocID%4TXcvJVnC$u(4NK>@o&!w{ zIGUzrMX8b%Eauib5r6ab!>rL#PXgYal0gZK*pnc>oz1l<{>`yT6&)V&Q>mHgq-uE` zSOn0`s>9fCqvB3IFM~rgQ>3(?oW%%M(WRQ_t;O%G<1DceEOCSw>*KYQ;&wFAvrlmUW8-S0khRe(x`R{xf{3#PtOLcZj za<6TD`eFFdNh`s21?33nMV8z4vD>%D5yMuGqMBAn36&Bw2hV%&{4p%N(zgWgt!GiO z763QL?;Y^7);!S0j7q~iEgX%oz`YwBV(|z4bsQU9uvZ|@g(*ledD;c-vNAThNeo1(Sh)q zntmF5z#v=Whk0tHN~Q`#akyv#_d~Z)?QONN72uXl3B!ftmyDseu?=Cj$F>r>1klRD zLXK<*1AbnY{{S3MSvE_TPf-*{Le)l)nyEM2SH}}JY3Q~>kVSTmI#}dKefC{fBK@%S z3z3QjMAp#MLv2HUWY~j(k$vaa~4s%NK#W}AsG zT}{KRnRIJUT8fvlpzJOO&jA*CdYu8U9}`0NBHcIn;x&plZp@78*6MDi{r3B?b;Q?m zXGzqws8_fn_h7>tPO9>9OBuXUi*;|VBZ|?bLZOwf(m*e(f2IfMqzfJ7NiMVdz^a~} zSWt)(F%^YGhQuiYbNFI)61Im7OsmWq_utR!+Y(x!zYm>-{qNh~<%u;hibs7`ducZs zS+Cc&7M7({$bFVySQG2#i$wJD$NE6>4_r3ieaDwvTSY%U?5WJBdtOj+|cIq#&!I5I7JS{P?;ZvIy->#p> z4s@W<;g?FF9@_a0{&*6mSW2UlW68UFxy-{;;vuc&{mMIaq*ZLmyC zq{qzVqzEnW2d_`Z3bwHhIvx7?;*oq*FDxq2o_BTtTGv}`<%f*P3(3RPQK5KNX%FGo zY&}$RXpqzx$X3>`a~0oJ_9vJT+SsL{r~|-yLB{lYv0Mkb)uToM{;^z8=5&a$%TegP zW=)zHR4YkSmdqiMk7IpDw?W$()j|u}uR+;hEXbik(Q zi1f1tEBxC?t>5(+9B0wL6xzNwQz4rC$VS{x==R$gHD+U3Pe}wh8Z?Gk2M~zUeJSlR z+oFPT=~iix=h+)j`bC{pROPc&G}Nv=b2lNsk!HOEnNdN&=6m04Ss~OR}mYaRU<|2If2`=V8r(Clk4_X5az>rL>?6TX=|nC7 zkB3jKG^pR&C%4ZUf_huUw!JNxI&W(Wwy&nx@83yV z@7E8S)Ntmlmhfd1tS;K^W|{rr^78qNQ@qo)es!2KH6Bk}NA`?w3v*n8cGM5Ds!ozE ziR!aKQByP(GFF*uj}1dik?yf+0R@pP0NVxX=37OnVSyVHGYKCW+#fG^m$Wp(U6Ooh{V- z@v4?;xn80pJlpY&hrZn1I(hWNMI@qr1wZJTK!W>w$G^)HQNa~FkyS4^msGkh*bj$H zQDymkEEJC={8a54JHEHLu_EAn8u*hb&X7-PkM*F|;P1_(uW#Ro%GH@^ufsId`Hv1D z+K!gJ`+?Dqd=Q~1%g=8USyDJdNZv4l_wB#qjx~Jn!9rOXs;S~NXyE%ip@H9V)97%o z4Q!FZt-MAT64VQB!AJu}-OceJXN}z0(xQe|Z$DO}a4b!_?SxYk&Z#36oX5g*@wZ%f zYRLE!0tw@z=x;87>B7#ELL-b5B=P#)8ynu;w!QHzaK>|2b`ez6LZzt4w=`_zTXB2a z=y2Q#;iIRGIMqu}ERP&b%VYxG1@`z1Q>`@8Qr5yPJwl+82~MI_n|-Wp-?l!)sjO*N zhs!HQjIy&K*aDwPT#%0{3293eWOz;2u$Wz0^M7+xk$H}!^-hTR8?-gf#oOJ z8hpEB#t5N~W{KLql1L4f<6??#!098WTs2bCicG4GCMyIpc&#tySo2x7-1|G>Rr1Gb zpVL5OV-#9?@fhwc*I+SM7H@gWu9{;

    EfOylh!LTU7`rx+jh0Zt^v0} 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 Date: Fri, 29 Aug 2025 10:16:55 +1000 Subject: [PATCH 002/195] bump licence of image --- internal/thumb/testdata/interop_index.jpg | Bin 540550 -> 540666 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/internal/thumb/testdata/interop_index.jpg b/internal/thumb/testdata/interop_index.jpg index ae78303df4ca2c1b7f417c53011109be9fd501d0..8c121ca141fb687617fce1a68a548df031e8a96c 100644 GIT binary patch delta 277 zcmZqsukh=?!UVB;#yr=G%rpiDPfsld4h9Aw7Gh*(U<9&2Sc;JqOy>dF(ol98kgWk# z!vtg-GBPo61L-;-HUhFmfOHoS&uC&|$N zr%2skM}>@%l7eC@ef{M8+}!-UV!iUr?9ANMl*~lE{GxRIa{bJ_l++5nf{X(DlFX8v zR9m;qoK!2{)SRUJ(xSZ7_~Pu!oJ;Mv#L^=Qf delta 156 zcmezMU!m>4!UVAthF`7~nQ06Ro}O9^91IK$>t)3=`YTCaW-7F Date: Fri, 29 Aug 2025 10:45:32 +1000 Subject: [PATCH 003/195] move colors/icc into thumbs/icc + asets --- assets/profiles/icc/NOTICE | 54 ++++++++++++++++++ .../icc/compatibleWithAdobeRGB1998.icc | Bin internal/thumb/icc.go | 21 +++++++ internal/thumb/vips.go | 1 - pkg/media/colors/icc.go | 51 ----------------- 5 files changed, 75 insertions(+), 52 deletions(-) create mode 100644 assets/profiles/icc/NOTICE rename {pkg/media/colors => assets/profiles}/icc/compatibleWithAdobeRGB1998.icc (100%) create mode 100644 internal/thumb/icc.go delete mode 100644 pkg/media/colors/icc.go diff --git a/assets/profiles/icc/NOTICE b/assets/profiles/icc/NOTICE new file mode 100644 index 000000000..661d66272 --- /dev/null +++ b/assets/profiles/icc/NOTICE @@ -0,0 +1,54 @@ + +Files: compatibleWithAdobeRGB1998.icc +Source: Debian icc-profiles-free + https://salsa.debian.org/debian/icc-profiles-free/-/tree/a7a3c11b8a6d3bc2937447183b87dc89de9d2388/icc-profiles-openicc/default_profiles/base +Copyright: Kai-Uwe Behrmann + Marti Maria + Photogamut + Graeme Gill + ColorSolutions +License: Zlib + +License: Zlib + The zlib/libpng License + . + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + . + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + . + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + . + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + . + 3. This notice may not be removed or altered from any source + distribution. + . + NO WARRANTY + . + BECAUSE THE DATA IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY + FOR THE DATA, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN + OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES + PROVIDE THE DATA "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED + OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS + TO THE QUALITY AND PERFORMANCE OF THE DATA IS WITH YOU. SHOULD THE + DATA PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, + REPAIR OR CORRECTION. + . + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING + WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR + REDISTRIBUTE THE DATA AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, + INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING + OUT OF THE USE OR INABILITY TO USE THE DATA (INCLUDING BUT NOT LIMITED + TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY + YOU OR THIRD PARTIES OR A FAILURE OF THE DATA TO OPERATE WITH ANY OTHER + PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGES. \ No newline at end of file diff --git a/pkg/media/colors/icc/compatibleWithAdobeRGB1998.icc b/assets/profiles/icc/compatibleWithAdobeRGB1998.icc similarity index 100% rename from pkg/media/colors/icc/compatibleWithAdobeRGB1998.icc rename to assets/profiles/icc/compatibleWithAdobeRGB1998.icc diff --git a/internal/thumb/icc.go b/internal/thumb/icc.go new file mode 100644 index 000000000..c08e47477 --- /dev/null +++ b/internal/thumb/icc.go @@ -0,0 +1,21 @@ +package thumb + +import ( + "os" + "path" +) + +/* +Possible TODO: move this into a shared pkg/ so non-thumb +consumers can also use it. However, it looks fiddly to hook that +up to `assets`, so I'm punting on that for now. +*/ + +func MustGetAdobeRGB1998Path() string { + p := path.Join(IccProfilesPath, "compatibleWithAdobeRGB1998.icc") + _, err := os.Stat(p) + if err != nil { + panic(err) + } + return p +} diff --git a/internal/thumb/vips.go b/internal/thumb/vips.go index 06509a5fb..faf18b8fd 100644 --- a/internal/thumb/vips.go +++ b/internal/thumb/vips.go @@ -9,7 +9,6 @@ import ( "github.com/photoprism/photoprism/pkg/clean" "github.com/photoprism/photoprism/pkg/fs" - "github.com/photoprism/photoprism/pkg/media/colors" ) // Vips generates a new thumbnail with the requested size and returns the file name and a buffer with the image bytes, diff --git a/pkg/media/colors/icc.go b/pkg/media/colors/icc.go deleted file mode 100644 index b2a45c610..000000000 --- a/pkg/media/colors/icc.go +++ /dev/null @@ -1,51 +0,0 @@ -package colors - -import ( - _ "embed" - "fmt" - "os" - "path" -) - -// this was pulled from the Debian package -// icc-profiles-free, where it is published with -// an MIT-style licence: -// https://salsa.debian.org/debian/icc-profiles-free/-/blob/master/icc-profiles-openicc/default_profiles/base/compatibleWithAdobeRGB1998.icc?ref_type=heads -// -//go:embed icc/compatibleWithAdobeRGB1998.icc -var compatibleWithAdobeRGB1998 []byte - -var compatibleWithAdobeRGB1998Path = "" - -var temporaryDirectory string = "" - -func getTemporaryDirectory() (string, error) { - if temporaryDirectory != "" { - return temporaryDirectory, nil - } - var err error - temporaryDirectory, err = os.MkdirTemp("", "photoprism-icc-") - if err != nil { - return "", fmt.Errorf("%w creating temp dir for ICC profiles", err) - } - return temporaryDirectory, nil -} - -func GetAdobeRGB1998Path() (p string, err error) { - if compatibleWithAdobeRGB1998Path != "" { - return compatibleWithAdobeRGB1998Path, nil - } - - tempDir, err := getTemporaryDirectory() - if err != nil { - return "", err - } - p = path.Join(tempDir, "compatibleWithAdobeRGB1998.icc") - - err = os.WriteFile(p, compatibleWithAdobeRGB1998, 0644) - if err != nil { - return "", fmt.Errorf("%w writing icc profile to temp dir", err) - } - compatibleWithAdobeRGB1998Path = p - return p, nil -} From 69c1c37197bf417876de2191f119d66fd0c2fd7f Mon Sep 17 00:00:00 2001 From: Jarrad Whitaker Date: Fri, 29 Aug 2025 10:45:43 +1000 Subject: [PATCH 004/195] tweak interopindex -> icc, fix typo, more comments --- internal/thumb/vips.go | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/internal/thumb/vips.go b/internal/thumb/vips.go index faf18b8fd..c17529eb6 100644 --- a/internal/thumb/vips.go +++ b/internal/thumb/vips.go @@ -76,26 +76,34 @@ func Vips(imageName string, imageBuffer []byte, hash, thumbPath string, width, h size = vips.SizeBoth } - iiFull := img.GetString("exif-ifd4-InteroperabilityIndex") - if iiFull != "" { + // Many cameras will define a JPEG's colour space by setting the InteroperabilityIndex + // tag instead of embedding an inline ICC profile. + // We detect this and embed explicit icc profiles for thumbs of such images, for the benefit of Vips + // and web browsers, none of which pay any attention to the InteropIndex tag. + if iiFull := img.GetString("exif-ifd4-InteroperabilityIndex"); iiFull != "" { + // according to my reading, I think [:4] should be e.g. "R98\x00". + // However, vips always returns [:4] = "R98 ", e.g. space instead of null. + // I'm pulling [:3] instead to paper over this - the exif spec says "4 bytes + // incl null terminator" so I think this is safe. ii := iiFull[:3] log.Tracef("read exif and got interopindex %s, %s", ii, iiFull) fallbackProfile := "" switch ii { - case "P98": + case "R03": + // adobe rgb + fallbackProfile = MustGetAdobeRGB1998Path() + case "R98": // srgb // we could logically embed an srgb profile in the image here, but // there's no value in doing so; everything assumes srgb anyway. - case "R03": - // adobe rgb - fallbackProfile, err = colors.GetAdobeRGB1998Path() - if err != nil { - log.Errorf("%s in %s (get fallback profile adobergb)", err, clean.Log(filepath.Base(imageName))) - } + case "THM": + // a thumbnail file. I can't find a ref on what colour space + // this is, so I'm assuming without evidence that they are also srgb. default: log.Warnf("exif in %s has unknown interop index %s", clean.Log(filepath.Base(imageName)), ii) } if fallbackProfile != "" { + // icc profile gets embedded here err = img.TransformICCProfileWithFallback(fallbackProfile, fallbackProfile) if err != nil { log.Errorf("%s in %s (set icc profile)", err, clean.Log(filepath.Base(imageName))) From 229df02bfb2dc6917506a298eda2c18963330877 Mon Sep 17 00:00:00 2001 From: Jarrad Whitaker Date: Fri, 29 Aug 2025 22:41:44 +1000 Subject: [PATCH 005/195] warn -> debug --- internal/thumb/vips.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/thumb/vips.go b/internal/thumb/vips.go index c17529eb6..52608856b 100644 --- a/internal/thumb/vips.go +++ b/internal/thumb/vips.go @@ -100,7 +100,7 @@ func Vips(imageName string, imageBuffer []byte, hash, thumbPath string, width, h // a thumbnail file. I can't find a ref on what colour space // this is, so I'm assuming without evidence that they are also srgb. default: - log.Warnf("exif in %s has unknown interop index %s", clean.Log(filepath.Base(imageName)), ii) + log.Debugf("exif in %s has unknown interop index %s", clean.Log(filepath.Base(imageName)), ii) } if fallbackProfile != "" { // icc profile gets embedded here From 2f5cc62d45f8d5eb06d8c296db4f1647d3d0391d Mon Sep 17 00:00:00 2001 From: Jarrad Whitaker Date: Fri, 29 Aug 2025 22:44:32 +1000 Subject: [PATCH 006/195] rename adobe rgb icc profile --- ...bleWithAdobeRGB1998.icc => adobe_rgb_compat.icc} | Bin internal/thumb/icc.go | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename assets/profiles/icc/{compatibleWithAdobeRGB1998.icc => adobe_rgb_compat.icc} (100%) diff --git a/assets/profiles/icc/compatibleWithAdobeRGB1998.icc b/assets/profiles/icc/adobe_rgb_compat.icc similarity index 100% rename from assets/profiles/icc/compatibleWithAdobeRGB1998.icc rename to assets/profiles/icc/adobe_rgb_compat.icc diff --git a/internal/thumb/icc.go b/internal/thumb/icc.go index c08e47477..e567aa430 100644 --- a/internal/thumb/icc.go +++ b/internal/thumb/icc.go @@ -12,7 +12,7 @@ up to `assets`, so I'm punting on that for now. */ func MustGetAdobeRGB1998Path() string { - p := path.Join(IccProfilesPath, "compatibleWithAdobeRGB1998.icc") + p := path.Join(IccProfilesPath, "adobe_rgb_compat.icc") _, err := os.Stat(p) if err != nil { panic(err) From fc37974015ed20e13229272f7c77bacd193bc14d Mon Sep 17 00:00:00 2001 From: Jarrad Whitaker Date: Sat, 30 Aug 2025 19:25:33 +1000 Subject: [PATCH 007/195] pull out interopindex/icc stuff to func, avoid clobbering existing profile --- internal/thumb/vips.go | 79 ++++++++++++++++++++++++------------------ 1 file changed, 46 insertions(+), 33 deletions(-) diff --git a/internal/thumb/vips.go b/internal/thumb/vips.go index 52608856b..4fd8f06a4 100644 --- a/internal/thumb/vips.go +++ b/internal/thumb/vips.go @@ -76,39 +76,8 @@ func Vips(imageName string, imageBuffer []byte, hash, thumbPath string, width, h size = vips.SizeBoth } - // Many cameras will define a JPEG's colour space by setting the InteroperabilityIndex - // tag instead of embedding an inline ICC profile. - // We detect this and embed explicit icc profiles for thumbs of such images, for the benefit of Vips - // and web browsers, none of which pay any attention to the InteropIndex tag. - if iiFull := img.GetString("exif-ifd4-InteroperabilityIndex"); iiFull != "" { - // according to my reading, I think [:4] should be e.g. "R98\x00". - // However, vips always returns [:4] = "R98 ", e.g. space instead of null. - // I'm pulling [:3] instead to paper over this - the exif spec says "4 bytes - // incl null terminator" so I think this is safe. - ii := iiFull[:3] - log.Tracef("read exif and got interopindex %s, %s", ii, iiFull) - fallbackProfile := "" - switch ii { - case "R03": - // adobe rgb - fallbackProfile = MustGetAdobeRGB1998Path() - case "R98": - // srgb - // we could logically embed an srgb profile in the image here, but - // there's no value in doing so; everything assumes srgb anyway. - case "THM": - // a thumbnail file. I can't find a ref on what colour space - // this is, so I'm assuming without evidence that they are also srgb. - default: - log.Debugf("exif in %s has unknown interop index %s", clean.Log(filepath.Base(imageName)), ii) - } - if fallbackProfile != "" { - // icc profile gets embedded here - err = img.TransformICCProfileWithFallback(fallbackProfile, fallbackProfile) - if err != nil { - log.Errorf("%s in %s (set icc profile)", err, clean.Log(filepath.Base(imageName))) - } - } + if err = vipsSetIccProfileForInteropIndex(img, clean.Log(filepath.Base(imageName))); err != nil { + log.Debugf("vips: %s in %s (set icc profile for interop index tag)", err, clean.Log(filepath.Base(imageName))) } // Create thumbnail image. @@ -231,3 +200,47 @@ func VipsRotate(img *vips.ImageRef, orientation int) error { return err } + +func vipsSetIccProfileForInteropIndex(img *vips.ImageRef, logName string) error { + + // Many cameras will define a JPEG's colour space by setting the InteroperabilityIndex + // tag instead of embedding an inline ICC profile. + // We detect this and embed explicit icc profiles for thumbs of such images, for the benefit of Vips + // and web browsers, none of which pay any attention to the InteropIndex tag. + iiFull := img.GetString("exif-ifd4-InteroperabilityIndex") + if iiFull == "" { + return nil + } + + // according to my reading, I think [:4] should be e.g. "R98\x00". + // However, vips always returns [:4] = "R98 ", e.g. space instead of null. + // I'm pulling [:3] instead to paper over this - the exif spec says "4 bytes + // incl null terminator" so I think this is safe. + ii := iiFull[:3] + log.Tracef("interopindex: %s read exif and got interopindex %s, %s", logName, ii, iiFull) + + if img.HasICCProfile() { + log.Debugf("interopindex: %s has both an interop index tag and an embedded ICC profile. ignoring.", logName) + return nil + } + + fallbackProfile := "" + switch ii { + case "R03": + // adobe rgb + fallbackProfile = MustGetAdobeRGB1998Path() + case "R98": + // srgb + // we could logically embed an srgb profile in the image here, but + // there's no value in doing so; everything assumes srgb anyway. + case "THM": + // a thumbnail file. I can't find a ref on what colour space + // this is, so I'm assuming without evidence that they are also srgb. + default: + log.Debugf("interopindex: %s has unknown interop index %s", logName, ii) + } + if fallbackProfile == "" { + return nil + } + return img.TransformICCProfileWithFallback(fallbackProfile, fallbackProfile) // icc profile gets embedded here +} From 264bc78d51375fc2c791ce6e8afb9162775d87e3 Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sat, 22 Nov 2025 09:24:28 +0100 Subject: [PATCH 008/195] Backend: Remove legacy Go build tags #5330 Signed-off-by: Michael Mayer --- internal/api/docs.go | 1 - internal/server/api_docs.go | 1 - internal/service/hub/debug.go | 1 - 3 files changed, 3 deletions(-) diff --git a/internal/api/docs.go b/internal/api/docs.go index 09ac2f652..4e105cf2a 100644 --- a/internal/api/docs.go +++ b/internal/api/docs.go @@ -1,5 +1,4 @@ //go:build debug -// +build debug package api diff --git a/internal/server/api_docs.go b/internal/server/api_docs.go index 58d50f66e..545f56807 100644 --- a/internal/server/api_docs.go +++ b/internal/server/api_docs.go @@ -1,5 +1,4 @@ //go:build debug -// +build debug package server diff --git a/internal/service/hub/debug.go b/internal/service/hub/debug.go index 5117d0039..16b968e17 100644 --- a/internal/service/hub/debug.go +++ b/internal/service/hub/debug.go @@ -1,5 +1,4 @@ //go:build debug -// +build debug package hub From 4eac10c9d14a452d120d94fc333bd32d139e3a4e Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sat, 22 Nov 2025 09:25:01 +0100 Subject: [PATCH 009/195] API: Apply "golangci-lint" recommendations #5330 Signed-off-by: Michael Mayer --- internal/api/albums.go | 8 +++---- internal/api/api.go | 7 +++--- internal/api/api_auth.go | 11 ++++------ internal/api/api_test.go | 4 ---- internal/api/batch_photos.go | 16 ++++++++------ internal/api/batch_photos_edit.go | 4 ++-- internal/api/batch_photos_edit_test.go | 12 +++++------ internal/api/cluster_nodes_register.go | 2 +- internal/api/config_options.go | 2 +- internal/api/covers_test.go | 4 ++-- internal/api/download_album.go | 2 +- internal/api/oauth_revoke.go | 15 +++++++------ internal/api/oauth_revoke_test.go | 2 +- internal/api/oauth_token.go | 16 ++++++++------ internal/api/oidc_login.go | 3 +-- internal/api/oidc_redirect.go | 22 +++++++++---------- internal/api/photo_label.go | 18 +++++++++++++--- internal/api/photo_unstack.go | 14 ++++++------ internal/api/server.go | 4 +--- internal/api/services_upload.go | 2 +- internal/api/session_create.go | 24 ++++++++++++--------- internal/api/session_test.go | 10 ++++----- internal/api/share.go | 4 ++-- internal/api/share_preview.go | 7 +++++- internal/api/share_test.go | 4 ++-- internal/api/users_passcode.go | 15 ++++++++----- internal/api/users_passcode_test.go | 8 +++---- internal/api/users_upload.go | 14 ++++++------ internal/api/users_upload_multipart_test.go | 12 ----------- internal/api/video.go | 4 +--- internal/api/vision_caption.go | 7 +++--- internal/api/vision_face_test.go | 4 ---- internal/api/vision_labels_test.go | 4 ---- internal/api/vision_nsfw_test.go | 4 ---- internal/api/zip.go | 3 ++- 35 files changed, 146 insertions(+), 146 deletions(-) diff --git a/internal/api/albums.go b/internal/api/albums.go index 2f50da1f3..bb08b8c54 100644 --- a/internal/api/albums.go +++ b/internal/api/albums.go @@ -565,13 +565,11 @@ func AddPhotosToAlbum(router *gin.RouterGroup) { // Find album by UID. album, err := query.AlbumByUID(uid) - if err != nil { + switch { + case err != nil, !album.HasID(): AbortAlbumNotFound(c) return - } else if !album.HasID() { - AbortAlbumNotFound(c) - return - } else if frm.Empty() { + case frm.Empty(): Abort(c, http.StatusBadRequest, i18n.ErrNoItemsSelected) return } diff --git a/internal/api/api.go b/internal/api/api.go index 3118fcdb8..08cfd33ef 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -25,11 +25,12 @@ Additional information can be found in our Developer Guide: package api import ( - _ "net/http" + // Blank imports register HTTP handlers, models, translations, and form bindings via init side effects. + _ "net/http" // register net/http handlers referenced by swagger - _ "github.com/gin-gonic/gin" + _ "github.com/gin-gonic/gin" // register gin metadata used by swaggo - _ "github.com/photoprism/photoprism/internal/auth/acl" + _ "github.com/photoprism/photoprism/internal/auth/acl" // embed ACL docs _ "github.com/photoprism/photoprism/internal/entity" _ "github.com/photoprism/photoprism/internal/entity/query" _ "github.com/photoprism/photoprism/internal/event" diff --git a/internal/api/api_auth.go b/internal/api/api_auth.go index 05ab72732..1b9bd11f4 100644 --- a/internal/api/api_auth.go +++ b/internal/api/api_auth.go @@ -35,13 +35,10 @@ func AuthAny(c *gin.Context, resource acl.Resource, perms acl.Permissions) (s *e c.Header(header.CacheControl, header.CacheControlNoStore) // Allow requests based on an access token for specific resources. - switch resource { - case acl.ResourceVision: - if perms.Contains(acl.ActionUse) && vision.ServiceApi && vision.ServiceKey != "" && vision.ServiceKey == authToken { - s = entity.NewSessionFromToken(c, authToken, acl.ResourceVision.String(), "service-key") - event.AuditInfo([]string{clientIp, "%s", "%s %s as %s", status.Granted}, s.RefID, perms.First(), string(resource), s.GetClientRole().String()) - return s - } + if resource == acl.ResourceVision && perms.Contains(acl.ActionUse) && vision.ServiceApi && vision.ServiceKey != "" && vision.ServiceKey == authToken { + s = entity.NewSessionFromToken(c, authToken, acl.ResourceVision.String(), "service-key") + event.AuditInfo([]string{clientIp, "%s", "%s %s as %s", status.Granted}, s.RefID, perms.First(), string(resource), s.GetClientRole().String()) + return s } // Find active session to perform authorization check or deny if no session was found. diff --git a/internal/api/api_test.go b/internal/api/api_test.go index 3c41e0f80..974756446 100644 --- a/internal/api/api_test.go +++ b/internal/api/api_test.go @@ -58,10 +58,6 @@ func (r *CloseableResponseRecorder) CloseNotify() <-chan bool { return r.closeCh } -func (r *CloseableResponseRecorder) closeClient() { - r.closeCh <- true -} - // NewApiTest returns new API test helper. func NewApiTest() (app *gin.Engine, router *gin.RouterGroup, conf *config.Config) { gin.SetMode(gin.TestMode) diff --git a/internal/api/batch_photos.go b/internal/api/batch_photos.go index 7b0483391..65bab6f25 100644 --- a/internal/api/batch_photos.go +++ b/internal/api/batch_photos.go @@ -329,25 +329,27 @@ func BatchPhotosDelete(router *gin.RouterGroup) { } // Get selection or all archived photos if f.All is true. - if len(frm.Photos) == 0 && !frm.All { + switch { + case len(frm.Photos) == 0 && !frm.All: Abort(c, http.StatusBadRequest, i18n.ErrNoItemsSelected) return - } else if frm.All { + case frm.All: photos, err = query.ArchivedPhotos(1000000, 0) - } else { + default: photos, err = query.SelectedPhotos(frm) } // Abort if the query failed or no photos were found. - if err != nil { + switch { + case err != nil: log.Errorf("archive: %s", err) Abort(c, http.StatusBadRequest, i18n.ErrNoItemsSelected) return - } else if len(photos) > 0 { - log.Infof("archive: deleting %s", english.Plural(len(photos), "photo", "photos")) - } else { + case len(photos) == 0: Abort(c, http.StatusBadRequest, i18n.ErrNoItemsSelected) return + default: + log.Infof("archive: deleting %s", english.Plural(len(photos), "photo", "photos")) } var deleted entity.Photos diff --git a/internal/api/batch_photos_edit.go b/internal/api/batch_photos_edit.go index f97612a85..7992c8229 100644 --- a/internal/api/batch_photos_edit.go +++ b/internal/api/batch_photos_edit.go @@ -73,7 +73,7 @@ func BatchPhotosEdit(router *gin.RouterGroup) { return } - preloadedPhotos := map[string]*entity.Photo{} + var preloadedPhotos map[string]*entity.Photo if hydrated, err := query.PhotoPreloadByUIDs(photos.UIDs()); err != nil { log.Errorf("batch: failed to preload photo selection: %s", err) @@ -107,7 +107,7 @@ func BatchPhotosEdit(router *gin.RouterGroup) { // Refresh selected photos from database? if !savedAny { // Don't refresh. - } else if photos, count, err = search.BatchPhotos(frm.Photos, s); err != nil { + } else if photos, _, err = search.BatchPhotos(frm.Photos, s); err != nil { log.Errorf("batch: %s (refresh selection)", clean.Error(err)) } diff --git a/internal/api/batch_photos_edit_test.go b/internal/api/batch_photos_edit_test.go index cfc7032e1..5ee16a080 100644 --- a/internal/api/batch_photos_edit_test.go +++ b/internal/api/batch_photos_edit_test.go @@ -141,7 +141,7 @@ func TestBatchPhotosEdit(t *testing.T) { // Check the save response values. saveValues := gjson.Get(saveBody, "values").Raw - //t.Logf("save values: %#v", saveValues) + // t.Logf("save values: %#v", saveValues) timezoneAfter := gjson.Get(saveValues, "TimeZone") assert.Equal(t, timezoneAfter.String(), timezoneBefore.String()) altitudeAfter := gjson.Get(saveValues, "Altitude") @@ -200,10 +200,10 @@ func TestBatchPhotosEdit(t *testing.T) { assert.Equal(t, takenAfter.String(), takenBefore.String()) takenLocalAfter := gjson.Get(saveValues, "TakenAtLocal") assert.Equal(t, takenLocalAfter.String(), takenLocalBefore.String()) - //TODO Uncomment once keywords may be supported - //keywordsAfter := gjson.Get(saveValues, "DetailsKeywords") - //assert.Equal(t, keywordsAfter.String(), keywordsBefore.String()) - //assert.Equal(t, editValues, saveValues) + // TODO Uncomment once keywords may be supported + // keywordsAfter := gjson.Get(saveValues, "DetailsKeywords") + // assert.Equal(t, keywordsAfter.String(), keywordsBefore.String()) + // assert.Equal(t, editValues, saveValues) }) t.Run("SuccessChangeLocationValues", func(t *testing.T) { // Create new API test instance. @@ -471,7 +471,7 @@ func TestBatchPhotosEdit(t *testing.T) { editPhotos := gjson.Get(editBody, "models").Array() assert.Equal(t, len(editPhotos), 2) editValues := gjson.Get(editBody, "values").Raw - //t.Logf(editValues) + // t.Logf(editValues) albumsBefore := gjson.Get(editValues, "Albums") assert.Contains(t, albumsBefore.String(), "{\"value\":\"as6sg6bipotaab19\",\"title\":\"IlikeFood\",\"mixed\":false,\"action\":\"none\"}") assert.Contains(t, albumsBefore.String(), "{\"value\":\"as6sg6bxpogaaba7\",\"title\":\"Christmas 2030\",\"mixed\":true,\"action\":\"none\"}") diff --git a/internal/api/cluster_nodes_register.go b/internal/api/cluster_nodes_register.go index 7afdcd5c8..1cc96c5ff 100644 --- a/internal/api/cluster_nodes_register.go +++ b/internal/api/cluster_nodes_register.go @@ -111,7 +111,7 @@ func ClusterNodesRegister(router *gin.RouterGroup) { } for i := 0; i < len(name); i++ { b := name[i] - if !(b == '-' || (b >= 'a' && b <= 'z') || (b >= '0' && b <= '9')) { + if b != '-' && (b < 'a' || b > 'z') && (b < '0' || b > '9') { event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register", "invalid name chars", status.Failed}) AbortBadRequest(c) return diff --git a/internal/api/config_options.go b/internal/api/config_options.go index 3b34e906b..5d25e64ff 100644 --- a/internal/api/config_options.go +++ b/internal/api/config_options.go @@ -72,7 +72,7 @@ func SaveConfigOptions(router *gin.RouterGroup) { v := make(entity.Values) if fs.FileExists(fileName) { - yamlData, err := os.ReadFile(fileName) + yamlData, err := os.ReadFile(fileName) // #nosec G304 file path validated above if err != nil { log.Errorf("config: failed loading values from %s (%s)", clean.Log(fileName), err) diff --git a/internal/api/covers_test.go b/internal/api/covers_test.go index cd655d943..7e21e9945 100644 --- a/internal/api/covers_test.go +++ b/internal/api/covers_test.go @@ -56,8 +56,8 @@ func TestLabelCover(t *testing.T) { t.Run("CouldNotFindOriginal", func(t *testing.T) { app, router, conf := NewApiTest() LabelCover(router) - //r := PerformRequest(app, "GET", "/api/v1/labels/ls6sg6b1wowuy3c3/t/"+conf.PreviewToken()+"/tile_500") - //ls6sg6b1wowuy3c2 + // r := PerformRequest(app, "GET", "/api/v1/labels/ls6sg6b1wowuy3c3/t/"+conf.PreviewToken()+"/tile_500") + // ls6sg6b1wowuy3c2 r := PerformRequest(app, "GET", "/api/v1/labels/ls6sg6b1wowuy3c2/t/"+conf.PreviewToken()+"/tile_500") assert.Equal(t, http.StatusOK, r.Code) }) diff --git a/internal/api/download_album.go b/internal/api/download_album.go index 8d41b4818..ea50436a6 100644 --- a/internal/api/download_album.go +++ b/internal/api/download_album.go @@ -130,7 +130,7 @@ func DownloadAlbum(router *gin.RouterGroup) { alias = file.DownloadName(dlName, seq) } - aliases[key] += 1 + aliases[key]++ if fs.FileExists(fileName) { if zipErr := fs.ZipFile(zipWriter, fileName, alias, false); zipErr != nil { diff --git a/internal/api/oauth_revoke.go b/internal/api/oauth_revoke.go index 926731c5c..dd37e5b8b 100644 --- a/internal/api/oauth_revoke.go +++ b/internal/api/oauth_revoke.go @@ -133,26 +133,27 @@ func OAuthRevoke(router *gin.RouterGroup) { } // Check revocation request and abort if invalid. - if err != nil { + switch { + case err != nil: event.AuditErr([]string{clientIp, "oauth2", actor, action, "delete %s as %s", status.Error(err)}, clean.Log(sess.RefID), role.String()) AbortInvalidCredentials(c) return - } else if sess == nil { - event.AuditErr([]string{clientIp, "oauth2", actor, action, "delete %s as %s", status.Denied}, clean.Log(sess.RefID), role.String()) + case sess == nil: + event.AuditErr([]string{clientIp, "oauth2", actor, action, "delete %s as %s", status.Denied}, "", role.String()) AbortInvalidCredentials(c) return - } else if sess.Abort(c) { + case sess.Abort(c): event.AuditErr([]string{clientIp, "oauth2", actor, action, "delete %s as %s", status.Denied}, clean.Log(sess.RefID), role.String()) return - } else if !sess.IsClient() { + case !sess.IsClient(): event.AuditErr([]string{clientIp, "oauth2", actor, action, "delete %s as %s", status.Denied}, clean.Log(sess.RefID), role.String()) c.AbortWithStatusJSON(http.StatusForbidden, i18n.NewResponse(http.StatusForbidden, i18n.ErrForbidden)) return - } else if sUserUID != "" && sess.UserUID != sUserUID { + case sUserUID != "" && sess.UserUID != sUserUID: event.AuditErr([]string{clientIp, "oauth2", actor, action, "delete %s as %s", authn.ErrUnauthorized.Error()}, clean.Log(sess.RefID), role.String()) AbortInvalidCredentials(c) return - } else { + default: event.AuditInfo([]string{clientIp, "oauth2", actor, action, "delete %s as %s", status.Granted}, clean.Log(sess.RefID), role.String()) } diff --git a/internal/api/oauth_revoke_test.go b/internal/api/oauth_revoke_test.go index 04948c590..730a776f6 100644 --- a/internal/api/oauth_revoke_test.go +++ b/internal/api/oauth_revoke_test.go @@ -18,7 +18,7 @@ import ( ) func TestOAuthRevoke(t *testing.T) { - const tokenPath = "/api/v1/oauth/token" + const tokenPath = "/api/v1/oauth/token" // #nosec G101 test constant, not a credential const revokePath = "/api/v1/oauth/revoke" t.Run("ClientSuccessToken", func(t *testing.T) { diff --git a/internal/api/oauth_token.go b/internal/api/oauth_token.go index 6cf796120..a0c1002c4 100644 --- a/internal/api/oauth_token.go +++ b/internal/api/oauth_token.go @@ -86,11 +86,12 @@ func OAuthToken(router *gin.RouterGroup) { return } - if frm.ClientID != "" { + switch { + case frm.ClientID != "": actor = fmt.Sprintf("client %s", clean.Log(frm.ClientID)) - } else if frm.Username != "" { + case frm.Username != "": actor = fmt.Sprintf("user %s", clean.Log(frm.Username)) - } else if frm.GrantType == authn.GrantPassword { + case frm.GrantType == authn.GrantPassword: actor = "unknown user" } @@ -150,17 +151,18 @@ func OAuthToken(router *gin.RouterGroup) { authUser, authProvider, authMethod, authErr := entity.Auth(loginForm, nil, c) - if authProvider.IsClient() { + switch { + case authProvider.IsClient(): event.AuditErr([]string{clientIp, "oauth2", actor, action, status.Denied}) AbortInvalidCredentials(c) return - } else if authMethod.Is(authn.Method2FA) && errors.Is(authErr, authn.ErrPasscodeRequired) { + case authMethod.Is(authn.Method2FA) && errors.Is(authErr, authn.ErrPasscodeRequired): // Ok. - } else if authErr != nil { + case authErr != nil: event.AuditErr([]string{clientIp, "oauth2", actor, action, status.Error(authErr)}) AbortInvalidCredentials(c) return - } else if !authUser.Equal(s.GetUser()) { + case !authUser.Equal(s.GetUser()): event.AuditErr([]string{clientIp, "oauth2", actor, action, authn.ErrUserDoesNotMatch.Error()}) AbortInvalidCredentials(c) return diff --git a/internal/api/oidc_login.go b/internal/api/oidc_login.go index 628ad8cfb..615802450 100644 --- a/internal/api/oidc_login.go +++ b/internal/api/oidc_login.go @@ -51,8 +51,7 @@ func OIDCLogin(router *gin.RouterGroup) { } // Check request rate limit. - var r *limiter.Request - r = limiter.Login.Request(clientIp) + r := limiter.Login.Request(clientIp) // Abort if failure rate limit is exceeded. if r.Reject() || limiter.Auth.Reject(clientIp) { diff --git a/internal/api/oidc_redirect.go b/internal/api/oidc_redirect.go index c5a1b783f..30ad88d0d 100644 --- a/internal/api/oidc_redirect.go +++ b/internal/api/oidc_redirect.go @@ -66,8 +66,7 @@ func OIDCRedirect(router *gin.RouterGroup) { } // Check request rate limit. - var r *limiter.Request - r = limiter.Login.Request(clientIp) + r := limiter.Login.Request(clientIp) // Abort if failure rate limit is exceeded. if r.Reject() || limiter.Auth.Reject(clientIp) { @@ -145,17 +144,18 @@ func OIDCRedirect(router *gin.RouterGroup) { event.AuditInfo([]string{clientIp, "create session", "oidc", "found user", userName}) // Check if the account is enabled and the OIDC Subject ID matches. - if !user.CanLogIn() { + switch { + case !user.CanLogIn(): event.AuditErr([]string{clientIp, "create session", "oidc", userName, authn.ErrAccountDisabled.Error()}) event.LoginError(clientIp, "oidc", userName, userAgent, authn.ErrAccountDisabled.Error()) c.HTML(http.StatusUnauthorized, "auth.gohtml", CreateSessionError(http.StatusUnauthorized, i18n.Error(i18n.ErrInvalidCredentials))) return - } else if authn.ProviderOIDC.NotEqual(user.AuthProvider) { + case authn.ProviderOIDC.NotEqual(user.AuthProvider): event.AuditErr([]string{clientIp, "create session", "oidc", userName, authn.ErrAuthProviderIsNotOIDC.Error()}) event.LoginError(clientIp, "oidc", userName, userAgent, authn.ErrAuthProviderIsNotOIDC.Error()) c.HTML(http.StatusUnauthorized, "auth.gohtml", CreateSessionError(http.StatusUnauthorized, i18n.Error(i18n.ErrInvalidCredentials))) return - } else if user.AuthID == "" || oidcUser.AuthID == "" || user.AuthID != oidcUser.AuthID { + case user.AuthID == "" || oidcUser.AuthID == "" || user.AuthID != oidcUser.AuthID: event.AuditErr([]string{clientIp, "create session", "oidc", userName, authn.ErrInvalidAuthID.Error()}) event.LoginError(clientIp, "oidc", userName, userAgent, authn.ErrInvalidAuthID.Error()) c.HTML(http.StatusUnauthorized, "auth.gohtml", CreateSessionError(http.StatusUnauthorized, i18n.Error(i18n.ErrInvalidCredentials))) @@ -228,12 +228,12 @@ func OIDCRedirect(router *gin.RouterGroup) { } // Set user avatar image? - if avatarUrl := userInfo.Picture; avatarUrl == "" || user.HasAvatar() { - // Do nothing. - } else if err = avatar.SetUserImageURL(user, avatarUrl, entity.SrcOIDC, conf.ThumbCachePath()); err != nil { - event.AuditWarn([]string{clientIp, "create session", "oidc", userName, "failed to set avatar image", err.Error()}) + if avatarUrl := userInfo.Picture; avatarUrl != "" && !user.HasAvatar() { + if err = avatar.SetUserImageURL(user, avatarUrl, entity.SrcOIDC, conf.ThumbCachePath()); err != nil { + event.AuditWarn([]string{clientIp, "create session", "oidc", userName, "failed to set avatar image", err.Error()}) + } } - } else if conf.UsersQuotaReached(conf.OIDCRole()) { + } else if conf.UsersQuotaReached(conf.OIDCRole()) { //nolint:gocritic userName = oidcUser.Username() event.AuditWarn([]string{clientIp, "create session", "oidc", "create user", userName, authn.ErrUsersQuotaExceeded.Error()}) event.LoginError(clientIp, "oidc", userName, userAgent, authn.ErrUsersQuotaExceeded.Error()) @@ -247,7 +247,7 @@ func OIDCRedirect(router *gin.RouterGroup) { // Resolve potential naming conflict by adding a random number to the username. if found := entity.FindUserByName(userName); found != nil { - userName = userName + rnd.Base10(6) + userName += rnd.Base10(6) } event.AuditInfo([]string{clientIp, "create session", "oidc", "create user", userName}) diff --git a/internal/api/photo_label.go b/internal/api/photo_label.go index 9d98adb70..c59057254 100644 --- a/internal/api/photo_label.go +++ b/internal/api/photo_label.go @@ -1,6 +1,7 @@ package api import ( + "errors" "net/http" "strconv" @@ -136,6 +137,11 @@ func RemovePhotoLabel(router *gin.RouterGroup) { return } + if labelId < 0 { + AbortBadRequest(c, errors.New("invalid label id")) + return + } + label, err := query.PhotoLabel(m.ID, uint(labelId)) if err != nil { @@ -143,13 +149,14 @@ func RemovePhotoLabel(router *gin.RouterGroup) { return } - if (label.LabelSrc == classify.SrcManual || label.LabelSrc == entity.SrcBatch) && label.Uncertainty < 100 { + switch { + case (label.LabelSrc == classify.SrcManual || label.LabelSrc == entity.SrcBatch) && label.Uncertainty < 100: logErr("label", entity.Db().Delete(&label).Error) - } else if label.LabelSrc != classify.SrcManual && label.LabelSrc != entity.SrcBatch { + case label.LabelSrc != classify.SrcManual && label.LabelSrc != entity.SrcBatch: label.Uncertainty = 100 label.LabelSrc = entity.SrcManual logErr("label", entity.Db().Save(&label).Error) - } else { + default: logErr("label", entity.Db().Save(&label).Error) } @@ -212,6 +219,11 @@ func UpdatePhotoLabel(router *gin.RouterGroup) { return } + if labelId < 0 { + AbortBadRequest(c, errors.New("invalid label id")) + return + } + label, err := query.PhotoLabel(m.ID, uint(labelId)) if err != nil { diff --git a/internal/api/photo_unstack.go b/internal/api/photo_unstack.go index 573b5907d..b90d88fe4 100644 --- a/internal/api/photo_unstack.go +++ b/internal/api/photo_unstack.go @@ -47,15 +47,16 @@ func PhotoUnstack(router *gin.RouterGroup) { return } - if file.FilePrimary { + switch { + case file.FilePrimary: log.Errorf("photo: cannot unstack primary file") AbortBadRequest(c) return - } else if file.FileSidecar { + case file.FileSidecar: log.Errorf("photo: cannot unstack sidecar files") AbortBadRequest(c) return - } else if file.FileRoot != entity.RootOriginals { + case file.FileRoot != entity.RootOriginals: log.Errorf("photo: only originals can be unstacked") AbortBadRequest(c) return @@ -91,15 +92,16 @@ func PhotoUnstack(router *gin.RouterGroup) { related, err := unstackFile.RelatedFiles(false) - if err != nil { + switch { + case err != nil: log.Errorf("photo: %s (unstack %s)", err, clean.Log(baseName)) AbortEntityNotFound(c) return - } else if related.Len() == 0 { + case related.Len() == 0: log.Errorf("photo: found no files for %s (unstack)", clean.Log(baseName)) AbortEntityNotFound(c) return - } else if related.Main == nil { + case related.Main == nil: log.Errorf("photo: found no main media file for %s (unstack)", clean.Log(baseName)) AbortEntityNotFound(c) return diff --git a/internal/api/server.go b/internal/api/server.go index 872cef4b9..ed13cbf14 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -6,7 +6,6 @@ import ( "github.com/gin-gonic/gin" "github.com/photoprism/photoprism/internal/auth/acl" - "github.com/photoprism/photoprism/internal/config" "github.com/photoprism/photoprism/internal/photoprism/get" "github.com/photoprism/photoprism/internal/server/process" ) @@ -32,8 +31,7 @@ func StopServer(router *gin.RouterGroup) { return } - var options *config.Options - options = conf.Options() + options := conf.Options() // Trigger restart. // diff --git a/internal/api/services_upload.go b/internal/api/services_upload.go index 21466eea8..c07bcded5 100644 --- a/internal/api/services_upload.go +++ b/internal/api/services_upload.go @@ -74,7 +74,7 @@ func UploadToService(router *gin.RouterGroup) { alias = file.ShareBase(seq) } - aliases[key] += 1 + aliases[key]++ entity.FirstOrCreateFileShare(entity.NewFileShare(file.ID, m.ID, alias)) } diff --git a/internal/api/session_create.go b/internal/api/session_create.go index 5a5e0dbbc..9414274ce 100644 --- a/internal/api/session_create.go +++ b/internal/api/session_create.go @@ -100,13 +100,14 @@ func CreateSession(router *gin.RouterGroup) { // Check authentication credentials. if err = sess.LogIn(frm, c); err != nil { - if sess.GetMethod().IsNot(authn.Method2FA) { + switch { + case sess.GetMethod().IsNot(authn.Method2FA): c.AbortWithStatusJSON(sess.HttpStatus(), gin.H{"error": i18n.Msg(i18n.ErrInvalidCredentials)}) - } else if errors.Is(err, authn.ErrPasscodeRequired) { + case errors.Is(err, authn.ErrPasscodeRequired): c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": err.Error(), "code": 32, "message": i18n.Msg(i18n.ErrPasscodeRequired)}) // Return the reserved request rate limit tokens if password is correct, even if the verification code is missing. r.Success() - } else { + default: c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": err.Error(), "code": http.StatusUnauthorized, "message": i18n.Msg(i18n.ErrInvalidPasscode)}) } return @@ -119,17 +120,20 @@ func CreateSession(router *gin.RouterGroup) { } // Save session after successful authentication. - if sess, err = get.Session().Save(sess); err != nil { - event.AuditErr([]string{clientIp, status.Error(err)}) + switch saved, saveErr := get.Session().Save(sess); { + case saveErr != nil: + event.AuditErr([]string{clientIp, status.Error(saveErr)}) c.AbortWithStatusJSON(sess.HttpStatus(), gin.H{"error": i18n.Msg(i18n.ErrInvalidCredentials)}) return - } else if sess == nil { + case saved == nil: c.AbortWithStatusJSON(sess.HttpStatus(), gin.H{"error": i18n.Msg(i18n.ErrUnexpected)}) return - } else if isNew { - event.AuditInfo([]string{clientIp, "session %s", "created"}, sess.RefID) - } else { - event.AuditInfo([]string{clientIp, "session %s", "updated"}, sess.RefID) + case isNew: + event.AuditInfo([]string{clientIp, "session %s", "created"}, saved.RefID) + sess = saved + default: + event.AuditInfo([]string{clientIp, "session %s", "updated"}, saved.RefID) + sess = saved } // Return the reserved request rate limit tokens after successful authentication. diff --git a/internal/api/session_test.go b/internal/api/session_test.go index f35963f5c..35732744a 100644 --- a/internal/api/session_test.go +++ b/internal/api/session_test.go @@ -72,7 +72,7 @@ func TestCreateSession(t *testing.T) { CreateSession(router) r := PerformRequestWithBody(app, http.MethodPost, "/api/v1/session", `{"username": "admin", "password": "photoprism"}`) - //t.Logf("Response Body: %s", r.Body.String()) + // t.Logf("Response Body: %s", r.Body.String()) userName := gjson.Get(r.Body.String(), "user.Name").String() assert.Equal(t, "admin", userName) assert.Equal(t, http.StatusOK, r.Code) @@ -373,7 +373,7 @@ func TestDeleteSession(t *testing.T) { defer conf.SetAuthMode(config.AuthModePublic) DeleteSession(router) - bobToken := "69be27ac5ca305b394046a83f6fda18167ca3d3f2dbe7ac1" + bobToken := "69be27ac5ca305b394046a83f6fda18167ca3d3f2dbe7ac1" // #nosec G101 test token r := PerformRequest(app, http.MethodDelete, "/api/v1/session/"+rnd.SessionID(bobToken)) assert.Equal(t, http.StatusUnauthorized, r.Code) @@ -399,7 +399,7 @@ func TestDeleteSession(t *testing.T) { DeleteSession(router) bobToken := AuthenticateUser(app, router, "bob", "Bobbob123!") - aliceToken := "69be27ac5ca305b394046a83f6fda18167ca3d3f2dbe7ac0" + aliceToken := "69be27ac5ca305b394046a83f6fda18167ca3d3f2dbe7ac0" // #nosec G101 test token r := AuthenticatedRequest(app, http.MethodDelete, "/api/v1/session/"+rnd.SessionID(aliceToken), bobToken) assert.Equal(t, http.StatusForbidden, r.Code) @@ -411,7 +411,7 @@ func TestDeleteSession(t *testing.T) { DeleteSession(router) aliceToken := AuthenticateUser(app, router, "alice", "Alice123!") - bobToken := "69be27ac5ca305b394046a83f6fda18167ca3d3f2dbe7ac1" + bobToken := "69be27ac5ca305b394046a83f6fda18167ca3d3f2dbe7ac1" // #nosec G101 test token r := AuthenticatedRequest(app, http.MethodDelete, "/api/v1/session/"+rnd.SessionID(bobToken), aliceToken) assert.Equal(t, http.StatusForbidden, r.Code) @@ -423,7 +423,7 @@ func TestDeleteSession(t *testing.T) { DeleteSession(router) authToken := AuthenticateUser(app, router, "alice", "Alice123!") - deleteToken := "638bffc9b86a8fda0d908ebee84a43930cb8d1e3507f4aa0" + deleteToken := "638bffc9b86a8fda0d908ebee84a43930cb8d1e3507f4aa0" // #nosec G101 test token r := AuthenticatedRequest(app, http.MethodDelete, "/api/v1/session/"+rnd.SessionID(deleteToken), authToken) assert.Equal(t, http.StatusForbidden, r.Code) diff --git a/internal/api/share.go b/internal/api/share.go index d4048c8e4..12c394582 100644 --- a/internal/api/share.go +++ b/internal/api/share.go @@ -36,7 +36,7 @@ func ShareToken(router *gin.RouterGroup) { } clientConfig := conf.ClientShare() - clientConfig.SiteUrl = clientConfig.SiteUrl + path.Join("s", token) + clientConfig.SiteUrl += path.Join("s", token) uri := conf.LibraryUri("/albums") c.HTML(http.StatusOK, "share.gohtml", gin.H{"shared": gin.H{"token": token, "uri": uri}, "config": clientConfig}) @@ -71,7 +71,7 @@ func ShareTokenShared(router *gin.RouterGroup) { uid := links[0].ShareUID clientConfig := conf.ClientShare() - clientConfig.SiteUrl = clientConfig.SiteUrl + path.Join("s", token, uid) + clientConfig.SiteUrl += path.Join("s", token, uid) clientConfig.SitePreview = clientConfig.SiteUrl + "/preview" if a, err := query.AlbumByUID(uid); err == nil { diff --git a/internal/api/share_preview.go b/internal/api/share_preview.go index add59d183..538d55f66 100644 --- a/internal/api/share_preview.go +++ b/internal/api/share_preview.go @@ -114,7 +114,7 @@ func SharePreview(router *gin.RouterGroup) { return } - size, _ := thumb.Sizes[thumb.Tile500] + size := thumb.Sizes[thumb.Tile500] images := make([]image.Image, 0, len(p)) @@ -147,6 +147,11 @@ func SharePreview(router *gin.RouterGroup) { // Create album preview from thumbnail images. preview, err := frame.Collage(frame.Polaroid, images) + if err != nil { + log.Warnf("preview collage: %v", err) + c.Redirect(http.StatusTemporaryRedirect, conf.SitePreview()) + return + } // Downsize from 1600x900 to 1200x675. preview = imaging.Resize(preview, 1200, 0, imaging.Lanczos) diff --git a/internal/api/share_test.go b/internal/api/share_test.go index 6568b5cc1..71853c498 100644 --- a/internal/api/share_test.go +++ b/internal/api/share_test.go @@ -14,7 +14,7 @@ func TestShareToken(t *testing.T) { r := PerformRequest(app, "GET", "/api/v1/xxx") assert.Equal(t, http.StatusTemporaryRedirect, r.Code) }) - //TODO Why does it panic? + // TODO Why does it panic? /*t.Run("ValidToken", func(t *testing.T) { app, router, _ := NewApiTest() ShareToken(router) @@ -30,7 +30,7 @@ func TestShareTokenShared(t *testing.T) { r := PerformRequest(app, "GET", "/api/v1/1jxf3jfn2k/ss6sg6bxpogaaba7") assert.Equal(t, http.StatusTemporaryRedirect, r.Code) }) - //TODO Why does it panic? + // TODO Why does it panic? /*t.Run("ValidTokenAndShare", func(t *testing.T) { app, router, _ := NewApiTest() ShareTokenShared(router) diff --git a/internal/api/users_passcode.go b/internal/api/users_passcode.go index a06aeab4d..fe9da2c38 100644 --- a/internal/api/users_passcode.go +++ b/internal/api/users_passcode.go @@ -336,20 +336,25 @@ func checkUserPasscodePassword(c *gin.Context, user *entity.User, password strin } // Check if user login credentials are valid. - if authUser, provider, method, authErr := entity.Auth(f, nil, c); method == authn.Method2FA && errors.Is(authErr, authn.ErrPasscodeRequired) { + authUser, provider, method, authErr := entity.Auth(f, nil, c) + + switch { + case method == authn.Method2FA && errors.Is(authErr, authn.ErrPasscodeRequired): return http.StatusOK, i18n.MsgVerified, nil - } else if authErr != nil { + case authErr != nil: // Abort if authentication has failed otherwise. return code, msg, authErr - } else if authUser == nil { + case authUser == nil: // Abort if account was not found. return code, msg, authn.ErrAccountNotFound - } else if !authUser.Equal(user) { + case !authUser.Equal(user): // Abort if user accounts do not match. return code, msg, authn.ErrUserDoesNotMatch - } else if !provider.SupportsPasscodeAuthentication() || method != authn.MethodDefault { + case !provider.SupportsPasscodeAuthentication() || method != authn.MethodDefault: // Abort if e.g. an app password was provided. return code, msg, authn.ErrInvalidCredentials + default: + return http.StatusOK, i18n.MsgVerified, nil } } diff --git a/internal/api/users_passcode_test.go b/internal/api/users_passcode_test.go index 60a444712..31a3bf7ce 100644 --- a/internal/api/users_passcode_test.go +++ b/internal/api/users_passcode_test.go @@ -229,7 +229,7 @@ func TestDeactivateUserPasscode(t *testing.T) { } func TestUserPasscode(t *testing.T) { - //create + // create app, router, conf := NewApiTest() conf.SetAuthMode(config.AuthModePasswd) defer conf.SetAuthMode(config.AuthModePublic) @@ -260,7 +260,7 @@ func TestUserPasscode(t *testing.T) { code, err := totp.GenerateCode(secret, time.Now()) - //confirm + // confirm ConfirmUserPasscode(router) if err != nil { @@ -288,7 +288,7 @@ func TestUserPasscode(t *testing.T) { assert.Empty(t, activatedAt) assert.NotEmpty(t, verifiedAt) - //activate + // activate ActivateUserPasscode(router) r = AuthenticatedRequestWithBody(app, "POST", "/api/v1/users/uqxetse3cy5eo9z2/passcode/activate", string(pcStr), sessId) @@ -300,7 +300,7 @@ func TestUserPasscode(t *testing.T) { assert.NotEmpty(t, activatedAt) assert.NotEmpty(t, verifiedAt) - //deactivate + // deactivate DeactivateUserPasscode(router) r = AuthenticatedRequestWithBody(app, "POST", "/api/v1/users/uqxetse3cy5eo9z2/passcode/deactivate", string(pcStr), sessId) diff --git a/internal/api/users_upload.go b/internal/api/users_upload.go index 0c1ffb153..7618df6bd 100644 --- a/internal/api/users_upload.go +++ b/internal/api/users_upload.go @@ -117,13 +117,14 @@ func UploadUserFiles(router *gin.RouterGroup) { fileType := fs.FileType(baseName) // Reject unsupported files and files with extensions that aren't allowed. - if fileType == fs.TypeUnknown { + switch { + case fileType == fs.TypeUnknown: log.Errorf("upload: rejected %s because it has an unsupported file extension", clean.Log(baseName)) continue - } else if allowedExt.Excludes(fileType.DefaultExt()) { + case allowedExt.Excludes(fileType.DefaultExt()): log.Errorf("upload: rejected %s because its extension is not allowed", clean.Log(baseName)) continue - } else if fileSizeLimit > 0 && file.Size > fileSizeLimit { + case fileSizeLimit > 0 && file.Size > fileSizeLimit: log.Errorf("upload: rejected %s because its size exceeds the file size limit", clean.Log(baseName)) continue } @@ -203,13 +204,14 @@ func UploadUserFiles(router *gin.RouterGroup) { for _, filename := range uploads { labels, nsfwErr := vision.DetectNSFW([]string{filename}, media.SrcLocal) - if nsfwErr != nil { + switch { + case nsfwErr != nil: log.Debug(nsfwErr) continue - } else if len(labels) < 1 { + case len(labels) < 1: log.Errorf("nsfw: model returned no result") continue - } else if labels[0].IsSafe() { + case labels[0].IsSafe(): continue } diff --git a/internal/api/users_upload_multipart_test.go b/internal/api/users_upload_multipart_test.go index e3a36c337..541d87615 100644 --- a/internal/api/users_upload_multipart_test.go +++ b/internal/api/users_upload_multipart_test.go @@ -74,18 +74,6 @@ func buildZipWithDirsAndFiles(dirs []string, files map[string][]byte) []byte { return zbuf.Bytes() } -func findUploadedFiles(t *testing.T, base string) []string { - t.Helper() - var out []string - _ = filepath.Walk(base, func(path string, info os.FileInfo, err error) error { - if err == nil && !info.IsDir() { - out = append(out, path) - } - return nil - }) - return out -} - // findUploadedFilesForToken lists files only under upload subfolders whose name ends with token suffix. func findUploadedFilesForToken(t *testing.T, base string, tokenSuffix string) []string { t.Helper() diff --git a/internal/api/video.go b/internal/api/video.go index bf1570db2..187726e74 100644 --- a/internal/api/video.go +++ b/internal/api/video.go @@ -94,10 +94,10 @@ func GetVideo(router *gin.RouterGroup) { // Get video bitrate, codec, and file type. videoFileType := f.Type() - videoCodec := f.FileCodec videoBitrate := f.Bitrate() videoFileName := photoprism.FileName(f.FileRoot, f.FileName) videoContentType := f.ContentType() + var videoCodec string // If the file has a hybrid photo/video format, try to find and send the embedded video data. if f.MediaType == entity.MediaLive { @@ -201,7 +201,5 @@ func GetVideo(router *gin.RouterGroup) { } else { c.File(videoFileName) } - - return }) } diff --git a/internal/api/vision_caption.go b/internal/api/vision_caption.go index e2db1e954..c8ed0f7fd 100644 --- a/internal/api/vision_caption.go +++ b/internal/api/vision_caption.go @@ -55,15 +55,16 @@ func PostVisionCaption(router *gin.RouterGroup) { // Run inference to generate a caption. result, model, err := vision.GenerateCaption(request.Images, media.SrcRemote) - if err != nil { + switch { + case err != nil: log.Errorf("vision: %s (caption)", err) c.JSON(http.StatusBadRequest, vision.NewApiError(request.GetId(), http.StatusBadRequest)) return - } else if model == nil { + case model == nil: log.Errorf("vision: no model specified (caption)") c.JSON(http.StatusInternalServerError, vision.NewApiError(request.GetId(), http.StatusInternalServerError)) return - } else if result == nil { + case result == nil: log.Errorf("vision: no result (caption)") c.JSON(http.StatusInternalServerError, vision.NewApiError(request.GetId(), http.StatusInternalServerError)) return diff --git a/internal/api/vision_face_test.go b/internal/api/vision_face_test.go index 3a3f2f396..f6114718a 100644 --- a/internal/api/vision_face_test.go +++ b/internal/api/vision_face_test.go @@ -166,10 +166,6 @@ func TestPostVisionFace(t *testing.T) { t.Fatal(apiErr) } - if apiResponse == nil { - t.Fatal("api response expected") - } - // t.Logf("error: %s", apiResponse.Err()) assert.Error(t, apiResponse.Err()) diff --git a/internal/api/vision_labels_test.go b/internal/api/vision_labels_test.go index 999ee5c8d..5d403ce59 100644 --- a/internal/api/vision_labels_test.go +++ b/internal/api/vision_labels_test.go @@ -117,10 +117,6 @@ func TestPostVisionLabels(t *testing.T) { t.Fatal(apiErr) } - if apiResponse == nil { - t.Fatal("api response expected") - } - t.Logf("error: %s", apiResponse.Err()) assert.Error(t, apiResponse.Err()) diff --git a/internal/api/vision_nsfw_test.go b/internal/api/vision_nsfw_test.go index a5a6972d9..fd46a8118 100644 --- a/internal/api/vision_nsfw_test.go +++ b/internal/api/vision_nsfw_test.go @@ -132,10 +132,6 @@ func TestPostVisionNsfw(t *testing.T) { t.Fatal(apiErr) } - if apiResponse == nil { - t.Fatal("api response expected") - } - // t.Logf("error: %s", apiResponse.Err()) assert.Error(t, apiResponse.Err()) diff --git a/internal/api/zip.go b/internal/api/zip.go index 664ffbbce..4c6e3685e 100644 --- a/internal/api/zip.go +++ b/internal/api/zip.go @@ -96,6 +96,7 @@ func ZipCreate(router *gin.RouterGroup) { // Create new zip file. var newZipFile *os.File + // #nosec G304 zip name derived from request if newZipFile, err = os.Create(zipFileName); err != nil { Error(c, http.StatusInternalServerError, err, i18n.ErrZipFailed) return @@ -124,7 +125,7 @@ func ZipCreate(router *gin.RouterGroup) { alias = file.DownloadName(dlName, seq) } - aliases[key] += 1 + aliases[key]++ if fs.FileExists(fileName) { if zipErr := fs.ZipFile(zipWriter, fileName, alias, false); zipErr != nil { From 01c8dd0a10570b0f44ee93a6ac88dbbe9120e07c Mon Sep 17 00:00:00 2001 From: graciousgrey Date: Sat, 22 Nov 2025 10:19:15 +0100 Subject: [PATCH 010/195] Tests: Add more test cases #271 --- .../photoprism/batch/apply_labels_test.go | 702 ++++++++++++++++++ 1 file changed, 702 insertions(+) diff --git a/internal/photoprism/batch/apply_labels_test.go b/internal/photoprism/batch/apply_labels_test.go index 4b84f0679..195fffd0b 100644 --- a/internal/photoprism/batch/apply_labels_test.go +++ b/internal/photoprism/batch/apply_labels_test.go @@ -365,6 +365,655 @@ func TestApplyLabels(t *testing.T) { t.Errorf("expected label source %s, got %s", entity.SrcBatch, updatedLabel.LabelSrc) } }) + t.Run("AddExistingLabelZeroConfidenceImage", func(t *testing.T) { + photo := entity.PhotoFixtures.Pointer("Photo10").PreloadLabels() + label := entity.LabelFixtures.Get("landscape") + + // Add label using FirstOrCreatePhotoLabel + photoLabel := entity.FirstOrCreatePhotoLabel(entity.NewPhotoLabel(photo.ID, label.ID, 100, entity.SrcImage)) + + if photoLabel == nil { + t.Fatal("failed to create photo label") + } + + // Finally, delete the added photo label to ensure a clean state. + t.Cleanup(func() { + _ = photoLabel.Delete() + }) + + // Verify initial state + if photoLabel.Uncertainty != 100 { + t.Errorf("expected uncertainty 100, got %d", photoLabel.Uncertainty) + } + + if photoLabel.LabelSrc != entity.SrcImage { + t.Errorf("expected label source %s, got %s", entity.SrcImage, photoLabel.LabelSrc) + } + + // Re-add same label via batch (should update to 100% confidence) + labels := Items{ + Items: []Item{ + {Action: ActionAdd, Value: label.LabelUID}}, Action: ActionUpdate, + } + + if err := ApplyLabels(photo, labels); err != nil { + t.Fatal(err) + } + + // Verify label confidence was updated + var updatedLabel entity.PhotoLabel + + if err := entity.Db().Where("photo_id = ? AND label_id = ?", photo.ID, label.ID).First(&updatedLabel).Error; err != nil { + t.Fatal(err) + } + + if updatedLabel.Uncertainty != 0 { + t.Errorf("expected uncertainty 0 (100%% confidence), got %d", updatedLabel.Uncertainty) + } + + if updatedLabel.LabelSrc != entity.SrcBatch { + t.Errorf("expected label source %s, got %s", entity.SrcBatch, updatedLabel.LabelSrc) + } + }) + t.Run("AddExistingLabel100ConfidenceImage", func(t *testing.T) { + photo := entity.PhotoFixtures.Pointer("Photo10").PreloadLabels() + label := entity.LabelFixtures.Get("landscape") + + // Add label using FirstOrCreatePhotoLabel + photoLabel := entity.FirstOrCreatePhotoLabel(entity.NewPhotoLabel(photo.ID, label.ID, 0, entity.SrcImage)) + + if photoLabel == nil { + t.Fatal("failed to create photo label") + } + + // Finally, delete the added photo label to ensure a clean state. + t.Cleanup(func() { + _ = photoLabel.Delete() + }) + + // Verify initial state + if photoLabel.Uncertainty != 0 { + t.Errorf("expected uncertainty 100, got %d", photoLabel.Uncertainty) + } + + if photoLabel.LabelSrc != entity.SrcImage { + t.Errorf("expected label source %s, got %s", entity.SrcImage, photoLabel.LabelSrc) + } + + // Re-add same label via batch (should update to 100% confidence) + labels := Items{ + Items: []Item{ + {Action: ActionAdd, Value: label.LabelUID}, + }, Action: ActionUpdate, + } + + if err := ApplyLabels(photo, labels); err != nil { + t.Fatal(err) + } + + // Verify label confidence was updated + var updatedLabel entity.PhotoLabel + + if err := entity.Db().Where("photo_id = ? AND label_id = ?", photo.ID, label.ID).First(&updatedLabel).Error; err != nil { + t.Fatal(err) + } + + if updatedLabel.Uncertainty != 0 { + t.Errorf("expected uncertainty 0 (100%% confidence), got %d", updatedLabel.Uncertainty) + } + + if updatedLabel.LabelSrc != entity.SrcBatch { + t.Errorf("expected label source %s, got %s", entity.SrcBatch, updatedLabel.LabelSrc) + } + }) + t.Run("AddExistingCaptionLabel", func(t *testing.T) { + photo := entity.PhotoFixtures.Pointer("Photo10").PreloadLabels() + label := entity.LabelFixtures.Get("landscape") + + // Add label with some uncertainty using FirstOrCreatePhotoLabel + photoLabel := entity.FirstOrCreatePhotoLabel(entity.NewPhotoLabel(photo.ID, label.ID, 50, entity.SrcCaption)) + + if photoLabel == nil { + t.Fatal("failed to create photo label") + } + + // Finally, delete the added photo label to ensure a clean state. + t.Cleanup(func() { + _ = photoLabel.Delete() + }) + + // Verify initial state + if photoLabel.Uncertainty != 50 { + t.Errorf("expected uncertainty 50, got %d", photoLabel.Uncertainty) + } + + if photoLabel.LabelSrc != entity.SrcCaption { + t.Errorf("expected label source %s, got %s", entity.SrcCaption, photoLabel.LabelSrc) + } + + // Re-add same label via batch (should update to 100% confidence) + labels := Items{ + Items: []Item{ + {Action: ActionAdd, Value: label.LabelUID}, + }, Action: ActionUpdate, + } + + if err := ApplyLabels(photo, labels); err != nil { + t.Fatal(err) + } + + // Verify label confidence was updated + var updatedLabel entity.PhotoLabel + + if err := entity.Db().Where("photo_id = ? AND label_id = ?", photo.ID, label.ID).First(&updatedLabel).Error; err != nil { + t.Fatal(err) + } + + if updatedLabel.Uncertainty != 0 { + t.Errorf("expected uncertainty 0 (100%% confidence), got %d", updatedLabel.Uncertainty) + } + + if updatedLabel.LabelSrc != entity.SrcBatch { + t.Errorf("expected label source %s, got %s", entity.SrcBatch, updatedLabel.LabelSrc) + } + }) + t.Run("AddExistingTitleLabelZeroConfidence", func(t *testing.T) { + photo := entity.PhotoFixtures.Pointer("Photo10").PreloadLabels() + label := entity.LabelFixtures.Get("landscape") + + // Add label using FirstOrCreatePhotoLabel + photoLabel := entity.FirstOrCreatePhotoLabel(entity.NewPhotoLabel(photo.ID, label.ID, 100, entity.SrcTitle)) + + if photoLabel == nil { + t.Fatal("failed to create photo label") + } + + // Finally, delete the added photo label to ensure a clean state. + t.Cleanup(func() { + _ = photoLabel.Delete() + }) + + // Verify initial state + if photoLabel.Uncertainty != 100 { + t.Errorf("expected uncertainty 100, got %d", photoLabel.Uncertainty) + } + + if photoLabel.LabelSrc != entity.SrcTitle { + t.Errorf("expected label source %s, got %s", entity.SrcTitle, photoLabel.LabelSrc) + } + + // Re-add same label via batch (should update to 100% confidence) + labels := Items{ + Items: []Item{ + {Action: ActionAdd, Value: label.LabelUID}, + }, Action: ActionUpdate, + } + + if err := ApplyLabels(photo, labels); err != nil { + t.Fatal(err) + } + + // Verify label confidence was updated + var updatedLabel entity.PhotoLabel + + if err := entity.Db().Where("photo_id = ? AND label_id = ?", photo.ID, label.ID).First(&updatedLabel).Error; err != nil { + t.Fatal(err) + } + + if updatedLabel.Uncertainty != 0 { + t.Errorf("expected uncertainty 0 (100%% confidence), got %d", updatedLabel.Uncertainty) + } + + if updatedLabel.LabelSrc != entity.SrcBatch { + t.Errorf("expected label source %s, got %s", entity.SrcBatch, updatedLabel.LabelSrc) + } + }) + t.Run("AddExistingTitleLabel100Confidence", func(t *testing.T) { + photo := entity.PhotoFixtures.Pointer("Photo10").PreloadLabels() + label := entity.LabelFixtures.Get("landscape") + + // Add label using FirstOrCreatePhotoLabel + photoLabel := entity.FirstOrCreatePhotoLabel(entity.NewPhotoLabel(photo.ID, label.ID, 0, entity.SrcTitle)) + + if photoLabel == nil { + t.Fatal("failed to create photo label") + } + + // Finally, delete the added photo label to ensure a clean state. + t.Cleanup(func() { + _ = photoLabel.Delete() + }) + + // Verify initial state + if photoLabel.Uncertainty != 0 { + t.Errorf("expected uncertainty 0, got %d", photoLabel.Uncertainty) + } + + if photoLabel.LabelSrc != entity.SrcTitle { + t.Errorf("expected label source %s, got %s", entity.SrcTitle, photoLabel.LabelSrc) + } + + // Re-add same label via batch (should update to 100% confidence) + labels := Items{ + Items: []Item{ + {Action: ActionAdd, Value: label.LabelUID}, + }, Action: ActionUpdate, + } + + if err := ApplyLabels(photo, labels); err != nil { + t.Fatal(err) + } + + // Verify label confidence was updated + var updatedLabel entity.PhotoLabel + + if err := entity.Db().Where("photo_id = ? AND label_id = ?", photo.ID, label.ID).First(&updatedLabel).Error; err != nil { + t.Fatal(err) + } + + if updatedLabel.Uncertainty != 0 { + t.Errorf("expected uncertainty 0 (100%% confidence), got %d", updatedLabel.Uncertainty) + } + + if updatedLabel.LabelSrc != entity.SrcBatch { + t.Errorf("expected label source %s, got %s", entity.SrcBatch, updatedLabel.LabelSrc) + } + }) + t.Run("AddExistingVisionLabel", func(t *testing.T) { + photo := entity.PhotoFixtures.Pointer("Photo10").PreloadLabels() + label := entity.LabelFixtures.Get("landscape") + + // Add label with some uncertainty using FirstOrCreatePhotoLabel + photoLabel := entity.FirstOrCreatePhotoLabel(entity.NewPhotoLabel(photo.ID, label.ID, 50, entity.SrcVision)) + + if photoLabel == nil { + t.Fatal("failed to create photo label") + } + + // Finally, delete the added photo label to ensure a clean state. + t.Cleanup(func() { + _ = photoLabel.Delete() + }) + + // Verify initial state + if photoLabel.Uncertainty != 50 { + t.Errorf("expected uncertainty 50, got %d", photoLabel.Uncertainty) + } + + if photoLabel.LabelSrc != entity.SrcVision { + t.Errorf("expected label source %s, got %s", entity.SrcVision, photoLabel.LabelSrc) + } + + // Re-add same label via batch (should update to 100% confidence) + labels := Items{ + Items: []Item{ + {Action: ActionAdd, Value: label.LabelUID}, + }, Action: ActionUpdate, + } + + if err := ApplyLabels(photo, labels); err != nil { + t.Fatal(err) + } + + // Verify label confidence was updated + var updatedLabel entity.PhotoLabel + + if err := entity.Db().Where("photo_id = ? AND label_id = ?", photo.ID, label.ID).First(&updatedLabel).Error; err != nil { + t.Fatal(err) + } + + if updatedLabel.Uncertainty != 0 { + t.Errorf("expected uncertainty 0 (100%% confidence), got %d", updatedLabel.Uncertainty) + } + + if updatedLabel.LabelSrc != entity.SrcBatch { + t.Errorf("expected label source %s, got %s", entity.SrcBatch, updatedLabel.LabelSrc) + } + }) + t.Run("AddExistingVisionLabelZeroConfidence", func(t *testing.T) { + photo := entity.PhotoFixtures.Pointer("Photo10").PreloadLabels() + label := entity.LabelFixtures.Get("landscape") + + // Add label using FirstOrCreatePhotoLabel + photoLabel := entity.FirstOrCreatePhotoLabel(entity.NewPhotoLabel(photo.ID, label.ID, 100, entity.SrcVision)) + + if photoLabel == nil { + t.Fatal("failed to create photo label") + } + + // Finally, delete the added photo label to ensure a clean state. + t.Cleanup(func() { + _ = photoLabel.Delete() + }) + + // Verify initial state + if photoLabel.Uncertainty != 100 { + t.Errorf("expected uncertainty 100, got %d", photoLabel.Uncertainty) + } + + if photoLabel.LabelSrc != entity.SrcVision { + t.Errorf("expected label source %s, got %s", entity.SrcVision, photoLabel.LabelSrc) + } + + // Re-add same label via batch (should update to 100% confidence) + labels := Items{ + Items: []Item{ + {Action: ActionAdd, Value: label.LabelUID}, + }, Action: ActionUpdate, + } + + if err := ApplyLabels(photo, labels); err != nil { + t.Fatal(err) + } + + // Verify label confidence was updated + var updatedLabel entity.PhotoLabel + + if err := entity.Db().Where("photo_id = ? AND label_id = ?", photo.ID, label.ID).First(&updatedLabel).Error; err != nil { + t.Fatal(err) + } + + if updatedLabel.Uncertainty != 0 { + t.Errorf("expected uncertainty 0 (100%% confidence), got %d", updatedLabel.Uncertainty) + } + + if updatedLabel.LabelSrc != entity.SrcBatch { + t.Errorf("expected label source %s, got %s", entity.SrcBatch, updatedLabel.LabelSrc) + } + }) + t.Run("AddExistingVisionLabel100Confidence", func(t *testing.T) { + photo := entity.PhotoFixtures.Pointer("Photo01").PreloadLabels() + label := entity.LabelFixtures.Get("landscape") + + // Add label using FirstOrCreatePhotoLabel + photoLabel := entity.FirstOrCreatePhotoLabel(entity.NewPhotoLabel(photo.ID, label.ID, 0, entity.SrcVision)) + + if photoLabel == nil { + t.Fatal("failed to create photo label") + } + + // Finally, delete the added photo label to ensure a clean state. + t.Cleanup(func() { + _ = photoLabel.Delete() + }) + + // Verify initial state + if photoLabel.Uncertainty != 0 { + t.Errorf("expected uncertainty 0, got %d", photoLabel.Uncertainty) + } + + if photoLabel.LabelSrc != entity.SrcVision { + t.Errorf("expected label source %s, got %s", entity.SrcVision, photoLabel.LabelSrc) + } + + // Re-add same label via batch (should update to 100% confidence) + labels := Items{ + Items: []Item{ + {Action: ActionAdd, Value: label.LabelUID}, + }, Action: ActionUpdate, + } + + if err := ApplyLabels(photo, labels); err != nil { + t.Fatal(err) + } + + // Verify label confidence was updated + var updatedLabel entity.PhotoLabel + + if err := entity.Db().Where("photo_id = ? AND label_id = ?", photo.ID, label.ID).First(&updatedLabel).Error; err != nil { + t.Fatal(err) + } + + if updatedLabel.Uncertainty != 0 { + t.Errorf("expected uncertainty 0 (100%% confidence), got %d", updatedLabel.Uncertainty) + } + + if updatedLabel.LabelSrc != entity.SrcBatch { + t.Errorf("expected label source %s, got %s", entity.SrcBatch, updatedLabel.LabelSrc) + } + }) + t.Run("KeepHigherPriorityLabelOnAdd", func(t *testing.T) { + photo := entity.PhotoFixtures.Pointer("Photo01").PreloadLabels() + label := entity.LabelFixtures.Get("landscape") + + // Add label with some uncertainty using FirstOrCreatePhotoLabel + photoLabel := entity.FirstOrCreatePhotoLabel(entity.NewPhotoLabel(photo.ID, label.ID, 50, entity.SrcAdmin)) + + if photoLabel == nil { + t.Fatal("failed to create photo label") + } + + // Finally, delete the added photo label to ensure a clean state. + t.Cleanup(func() { + _ = photoLabel.Delete() + }) + + // Verify initial state + if photoLabel.Uncertainty != 50 { + t.Errorf("expected uncertainty 50, got %d", photoLabel.Uncertainty) + } + + if photoLabel.LabelSrc != entity.SrcAdmin { + t.Errorf("expected label source %s, got %s", entity.SrcAdmin, photoLabel.LabelSrc) + } + + // Re-add same label via batch (should update to 100% confidence) + labels := Items{ + Items: []Item{ + {Action: ActionAdd, Value: label.LabelUID}}, Action: ActionUpdate, + } + + if err := ApplyLabels(photo, labels); err != nil { + t.Fatal(err) + } + + // Verify label confidence was not updated + var updatedLabel entity.PhotoLabel + + if err := entity.Db().Where("photo_id = ? AND label_id = ?", photo.ID, label.ID).First(&updatedLabel).Error; err != nil { + t.Fatal(err) + } + + if updatedLabel.Uncertainty != 50 { + t.Errorf("expected uncertainty 50 (100%% confidence), got %d", updatedLabel.Uncertainty) + } + + if updatedLabel.LabelSrc != entity.SrcAdmin { + t.Errorf("expected label source %s, got %s", entity.SrcAdmin, updatedLabel.LabelSrc) + } + }) + t.Run("KeepHigherPriorityLabelOnAddZeroConfidence", func(t *testing.T) { + photo := entity.PhotoFixtures.Pointer("Photo54").PreloadLabels() + label := entity.LabelFixtures.Get("flower") + + // Add label using FirstOrCreatePhotoLabel + photoLabel := entity.FirstOrCreatePhotoLabel(entity.NewPhotoLabel(photo.ID, label.ID, 100, entity.SrcAdmin)) + + if photoLabel == nil { + t.Fatal("failed to create photo label") + } + + // Finally, delete the added photo label to ensure a clean state. + t.Cleanup(func() { + _ = photoLabel.Delete() + }) + + // Verify initial state + if photoLabel.Uncertainty != 100 { + t.Errorf("expected uncertainty 100, got %d", photoLabel.Uncertainty) + } + + if photoLabel.LabelSrc != entity.SrcAdmin { + t.Errorf("expected label source %s, got %s", entity.SrcAdmin, photoLabel.LabelSrc) + } + + labels := Items{Items: []Item{{Action: ActionAdd, Value: label.LabelUID}}, Action: ActionUpdate} + + if err := ApplyLabels(photo, labels); err != nil { + t.Fatal(err) + } + + var updated entity.PhotoLabel + + if err := entity.Db().Where("photo_id = ? AND label_id = ?", photo.ID, label.ID).First(&updated).Error; err != nil { + t.Fatal(err) + } + + if updated.Uncertainty != 100 { + t.Errorf("expected uncertainty to remain 100, got %d", updated.Uncertainty) + } + + if updated.LabelSrc != entity.SrcAdmin { + t.Errorf("expected label source %s, got %s", entity.SrcAdmin, updated.LabelSrc) + } + }) + t.Run("KeepHigherPriorityLabelOnAdd100Confidence", func(t *testing.T) { + photo := entity.PhotoFixtures.Pointer("Photo53").PreloadLabels() + label := entity.LabelFixtures.Get("landscape") + + // Add label using FirstOrCreatePhotoLabel + photoLabel := entity.FirstOrCreatePhotoLabel(entity.NewPhotoLabel(photo.ID, label.ID, 0, entity.SrcAdmin)) + + if photoLabel == nil { + t.Fatal("failed to create photo label") + } + + // Finally, delete the added photo label to ensure a clean state. + t.Cleanup(func() { + _ = photoLabel.Delete() + }) + + // Verify initial state + if photoLabel.Uncertainty != 0 { + t.Errorf("expected uncertainty 0, got %d", photoLabel.Uncertainty) + } + + if photoLabel.LabelSrc != entity.SrcAdmin { + t.Errorf("expected label source %s, got %s", entity.SrcAdmin, photoLabel.LabelSrc) + } + + labels := Items{Items: []Item{{Action: ActionAdd, Value: label.LabelUID}}, Action: ActionUpdate} + + if err := ApplyLabels(photo, labels); err != nil { + t.Fatal(err) + } + + var updated entity.PhotoLabel + + if err := entity.Db().Where("photo_id = ? AND label_id = ?", photo.ID, label.ID).First(&updated).Error; err != nil { + t.Fatal(err) + } + + if updated.Uncertainty != 0 { + t.Errorf("expected uncertainty to remain 0, got %d", updated.Uncertainty) + } + + if updated.LabelSrc != entity.SrcAdmin { + t.Errorf("expected label source %s, got %s", entity.SrcAdmin, updated.LabelSrc) + } + }) + t.Run("AddExistingManualLabelZeroConfidence", func(t *testing.T) { + photo := entity.PhotoFixtures.Pointer("Photo10").PreloadLabels() + label := entity.LabelFixtures.Get("landscape") + + // Add label using FirstOrCreatePhotoLabel + photoLabel := entity.FirstOrCreatePhotoLabel(entity.NewPhotoLabel(photo.ID, label.ID, 100, entity.SrcManual)) + + if photoLabel == nil { + t.Fatal("failed to create photo label") + } + + // Finally, delete the added photo label to ensure a clean state. + t.Cleanup(func() { + _ = photoLabel.Delete() + }) + + // Verify initial state + if photoLabel.Uncertainty != 100 { + t.Errorf("expected uncertainty 100, got %d", photoLabel.Uncertainty) + } + + if photoLabel.LabelSrc != entity.SrcManual { + t.Errorf("expected label source %s, got %s", entity.SrcManual, photoLabel.LabelSrc) + } + + // Re-add same label via batch (should update to 100% confidence) + labels := Items{ + Items: []Item{ + {Action: ActionAdd, Value: label.LabelUID}, + }, Action: ActionUpdate, + } + + if err := ApplyLabels(photo, labels); err != nil { + t.Fatal(err) + } + + // Verify label confidence was updated + var updatedLabel entity.PhotoLabel + + if err := entity.Db().Where("photo_id = ? AND label_id = ?", photo.ID, label.ID).First(&updatedLabel).Error; err != nil { + t.Fatal(err) + } + + if updatedLabel.Uncertainty != 0 { + t.Errorf("expected uncertainty 0 (100%% confidence), got %d", updatedLabel.Uncertainty) + } + + if updatedLabel.LabelSrc != entity.SrcBatch { + t.Errorf("expected label source %s, got %s", entity.SrcBatch, updatedLabel.LabelSrc) + } + }) + t.Run("AddExistingManualLabel100Confidence", func(t *testing.T) { + photo := entity.PhotoFixtures.Pointer("Photo10").PreloadLabels() + label := entity.LabelFixtures.Get("landscape") + + // Add label using FirstOrCreatePhotoLabel + photoLabel := entity.FirstOrCreatePhotoLabel(entity.NewPhotoLabel(photo.ID, label.ID, 0, entity.SrcManual)) + + if photoLabel == nil { + t.Fatal("failed to create photo label") + } + + // Finally, delete the added photo label to ensure a clean state. + t.Cleanup(func() { + _ = photoLabel.Delete() + }) + + // Verify initial state + if photoLabel.Uncertainty != 0 { + t.Errorf("expected uncertainty 0, got %d", photoLabel.Uncertainty) + } + + if photoLabel.LabelSrc != entity.SrcManual { + t.Errorf("expected label source %s, got %s", entity.SrcManual, photoLabel.LabelSrc) + } + + // Re-add same label via batch (should not update) + labels := Items{ + Items: []Item{ + {Action: ActionAdd, Value: label.LabelUID}, + }, Action: ActionUpdate, + } + + if err := ApplyLabels(photo, labels); err != nil { + t.Fatal(err) + } + + // Verify label confidence was updated + var updatedLabel entity.PhotoLabel + + if err := entity.Db().Where("photo_id = ? AND label_id = ?", photo.ID, label.ID).First(&updatedLabel).Error; err != nil { + t.Fatal(err) + } + + if updatedLabel.Uncertainty != 0 { + t.Errorf("expected uncertainty 0 (100%% confidence), got %d", updatedLabel.Uncertainty) + } + + if updatedLabel.LabelSrc != entity.SrcBatch { + t.Errorf("expected label source %s, got %s", entity.SrcBatch, updatedLabel.LabelSrc) + } + }) t.Run("AddExistingBatchLabelZeroConfidence", func(t *testing.T) { // Load labels from database. photo := entity.PhotoFixtures.Pointer("Photo10").PreloadLabels() @@ -418,6 +1067,59 @@ func TestApplyLabels(t *testing.T) { t.Errorf("expected label source %s, got %s", entity.SrcBatch, updatedLabel.LabelSrc) } }) + t.Run("AddExistingBatchLabel100Confidence", func(t *testing.T) { + photo := entity.PhotoFixtures.Pointer("Photo10").PreloadLabels() + label := entity.LabelFixtures.Get("landscape") + + // Add label using FirstOrCreatePhotoLabel + photoLabel := entity.FirstOrCreatePhotoLabel(entity.NewPhotoLabel(photo.ID, label.ID, 0, entity.SrcBatch)) + + if photoLabel == nil { + t.Fatal("failed to create photo label") + } + + // Finally, delete the added photo label to ensure a clean state. + t.Cleanup(func() { + _ = photoLabel.Delete() + }) + + // Verify initial state + if photoLabel.Uncertainty != 0 { + t.Errorf("expected uncertainty 0, got %d", photoLabel.Uncertainty) + } + + if photoLabel.LabelSrc != entity.SrcBatch { + t.Errorf("expected label source %s, got %s", entity.SrcBatch, photoLabel.LabelSrc) + } + + // Re-add same label via batch (should update to 100% confidence) + labels := Items{ + Items: []Item{ + {Action: ActionAdd, Value: label.LabelUID}, + }, + Action: ActionUpdate, + } + + if err := ApplyLabels(photo, labels); err != nil { + t.Fatal(err) + } + + // Verify label confidence was updated + var updatedLabel entity.PhotoLabel + + if err := entity.Db().Where("photo_id = ? AND label_id = ?", photo.ID, label.ID).First(&updatedLabel).Error; err != nil { + t.Fatal(err) + } + + if updatedLabel.Uncertainty != 0 { + t.Errorf("expected uncertainty 0 (100%% confidence), got %d", updatedLabel.Uncertainty) + } + + if updatedLabel.LabelSrc != entity.SrcBatch { + t.Errorf("expected label source %s, got %s", entity.SrcBatch, updatedLabel.LabelSrc) + } + }) + t.Run("InvalidPhotoReturnsError", func(t *testing.T) { labels := Items{ Items: []Item{ From cc651a84d005646a546905de13f30d2c16837094 Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sat, 22 Nov 2025 10:20:47 +0100 Subject: [PATCH 011/195] Workers: Apply "golangci-lint" recommendations #5330 Signed-off-by: Michael Mayer --- internal/workers/auto/import.go | 2 +- internal/workers/auto/index.go | 2 +- internal/workers/scheduler.go | 4 +++- internal/workers/sync.go | 7 ++++--- internal/workers/sync_download.go | 1 + internal/workers/vision.go | 7 +------ 6 files changed, 11 insertions(+), 12 deletions(-) diff --git a/internal/workers/auto/import.go b/internal/workers/auto/import.go index 9e2f0b237..ed291c486 100644 --- a/internal/workers/auto/import.go +++ b/internal/workers/auto/import.go @@ -43,7 +43,7 @@ func mustImport(delay time.Duration) bool { importMutex.Lock() defer importMutex.Unlock() - return !autoImport.IsZero() && autoImport.Sub(time.Now()) < -1*delay && !mutex.IndexWorker.Running() + return !autoImport.IsZero() && time.Until(autoImport) < -1*delay && !mutex.IndexWorker.Running() } // Import starts importing originals e.g. after WebDAV uploads. diff --git a/internal/workers/auto/index.go b/internal/workers/auto/index.go index 02a3b1124..30a4420d4 100644 --- a/internal/workers/auto/index.go +++ b/internal/workers/auto/index.go @@ -39,7 +39,7 @@ func mustIndex(delay time.Duration) bool { indexMutex.Lock() defer indexMutex.Unlock() - return !autoIndex.IsZero() && autoIndex.Sub(time.Now()) < -1*delay && !mutex.IndexWorker.Running() + return !autoIndex.IsZero() && time.Until(autoIndex) < -1*delay && !mutex.IndexWorker.Running() } // Index starts indexing originals e.g. after WebDAV uploads. diff --git a/internal/workers/scheduler.go b/internal/workers/scheduler.go index 6c036639a..eff3d728f 100644 --- a/internal/workers/scheduler.go +++ b/internal/workers/scheduler.go @@ -7,7 +7,9 @@ import ( ) var ( - Jobs map[string]gocron.Job + // Jobs keeps the scheduled background jobs keyed by name. + Jobs map[string]gocron.Job + // Scheduler is the shared job scheduler instance. Scheduler gocron.Scheduler ) diff --git a/internal/workers/sync.go b/internal/workers/sync.go index aa415a684..a6474b8ed 100644 --- a/internal/workers/sync.go +++ b/internal/workers/sync.go @@ -97,11 +97,12 @@ func (w *Sync) Start() (err error) { accErrors = 0 accError = "" - if a.SyncDownload { + switch { + case a.SyncDownload: syncStatus = entity.SyncStatusDownload - } else if a.SyncUpload { + case a.SyncUpload: syncStatus = entity.SyncStatusUpload - } else { + default: syncStatus = entity.SyncStatusSynced syncDate.Time = time.Now() syncDate.Valid = true diff --git a/internal/workers/sync_download.go b/internal/workers/sync_download.go index 2bc19b5be..853dd8ab9 100644 --- a/internal/workers/sync_download.go +++ b/internal/workers/sync_download.go @@ -17,6 +17,7 @@ import ( "github.com/photoprism/photoprism/pkg/fs" ) +// Downloads groups files to sync by their directory/prefix. type Downloads map[string][]entity.FileSync // downloadPath returns a temporary download path. diff --git a/internal/workers/vision.go b/internal/workers/vision.go index 597432d9a..c62b81984 100644 --- a/internal/workers/vision.go +++ b/internal/workers/vision.go @@ -83,11 +83,6 @@ func (w *Vision) scheduledModels() []string { return models } -// originalsPath returns the path that holds original media files. -func (w *Vision) originalsPath() string { - return w.conf.OriginalsPath() -} - // Start runs the requested vision models against photos matching the search // filter. `customSrc` allows the caller to override the metadata source string, // `force` regenerates metadata regardless of existing values, and `runType` @@ -194,7 +189,7 @@ func (w *Vision) Start(filter string, count int, models []string, customSrc stri generateCaptions := updateCaptions && m.ShouldGenerateCaption(customSrc, force) detectNsfw := updateNsfw && (!photo.PhotoPrivate || force) - if !(generateLabels || generateCaptions || detectNsfw || detectFaces) { + if !generateLabels && !generateCaptions && !detectNsfw && !detectFaces { continue } From 24e29a89ffcf90021daa0723ebf6b59a137aed68 Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sat, 22 Nov 2025 10:21:59 +0100 Subject: [PATCH 012/195] Backend: Apply linter recommendations to "photoprism/dl" package #5330 Signed-off-by: Michael Mayer --- internal/photoprism/dl/cmd.go | 6 +++--- internal/photoprism/dl/file.go | 8 +++++--- internal/photoprism/dl/info.go | 12 ++++++------ internal/photoprism/dl/options.go | 16 ++++++++-------- internal/photoprism/dl/options_headers_test.go | 11 ++++++----- internal/photoprism/dl/version.go | 2 +- internal/photoprism/dl/version_test.go | 2 ++ 7 files changed, 31 insertions(+), 26 deletions(-) diff --git a/internal/photoprism/dl/cmd.go b/internal/photoprism/dl/cmd.go index 5a4b40905..c5e9eae7f 100644 --- a/internal/photoprism/dl/cmd.go +++ b/internal/photoprism/dl/cmd.go @@ -21,15 +21,15 @@ func ytDlpCommand(ctx context.Context, args []string) *exec.Cmd { if sh == "" { sh = "bash" } - return exec.CommandContext(ctx, sh, append([]string{bin}, args...)...) + return exec.CommandContext(ctx, sh, append([]string{bin}, args...)...) // #nosec G204 command constructed from validated yt-dlp path and args } - return exec.CommandContext(ctx, bin, args...) + return exec.CommandContext(ctx, bin, args...) // #nosec G204 command constructed from validated yt-dlp path and args } // isShellScript tries to detect if a file starts with a shebang (#!). func isShellScript(path string) bool { - f, err := os.Open(path) + f, err := os.Open(path) // #nosec G304 path provided by FindYtDlpBin if err != nil { return false } diff --git a/internal/photoprism/dl/file.go b/internal/photoprism/dl/file.go index d4a36c6c0..2778d1ea2 100644 --- a/internal/photoprism/dl/file.go +++ b/internal/photoprism/dl/file.go @@ -26,6 +26,7 @@ func (result Metadata) DownloadToFileWithOptions( out := outTpl out = strings.ReplaceAll(out, "%(id)s", "abc") out = strings.ReplaceAll(out, "%(ext)s", "mp4") + // #nosec G301 media download directory should be accessible to user if err := os.MkdirAll(filepath.Dir(out), 0o755); err != nil { return nil, err } @@ -33,6 +34,7 @@ func (result Metadata) DownloadToFileWithOptions( if content == "" { content = "dummy" } + // #nosec G306 downloaded media files are intended to be user-readable if err := os.WriteFile(out, []byte(content), 0o644); err != nil { return nil, err } @@ -59,7 +61,7 @@ func (result Metadata) DownloadToFileWithOptions( if !result.Options.noInfoDownload { jsonTempPath = filepath.Join(tempPath, "info.json") if err := os.WriteFile(jsonTempPath, result.RawJSON, 0600); err != nil { - os.RemoveAll(tempPath) + _ = os.RemoveAll(tempPath) return nil, err } } @@ -94,12 +96,12 @@ func (result Metadata) DownloadToFileWithOptions( if result.Options.PlaylistStart > 0 { cmd.Args = append(cmd.Args, - "--playlist-start", strconv.Itoa(int(result.Options.PlaylistStart)), + "--playlist-start", strconv.FormatUint(uint64(result.Options.PlaylistStart), 10), ) } if result.Options.PlaylistEnd > 0 { cmd.Args = append(cmd.Args, - "--playlist-end", strconv.Itoa(int(result.Options.PlaylistEnd)), + "--playlist-end", strconv.FormatUint(uint64(result.Options.PlaylistEnd), 10), ) } if result.Options.FlatPlaylist { diff --git a/internal/photoprism/dl/info.go b/internal/photoprism/dl/info.go index 0b898659c..f1fbe8a76 100644 --- a/internal/photoprism/dl/info.go +++ b/internal/photoprism/dl/info.go @@ -59,8 +59,8 @@ type Info struct { ChapterNumber float64 `json:"chapter_number"` // Number of the chapter the video belongs to ChapterID string `json:"chapter_id"` // Id of the chapter the video belongs to - // Available for the video that is an episode of some series or programme: - Series string `json:"series"` // Title of the series or programme the video episode belongs to + // Available for the video that is an episode of some series or program: + Series string `json:"series"` // Title of the series or program the video episode belongs to Season string `json:"season"` // Title of the season the video episode belongs to SeasonNumber float64 `json:"season_number"` // Number of the season the video episode belongs to SeasonID string `json:"season_id"` // Id of the season the video episode belongs to @@ -171,12 +171,12 @@ func infoFromURL( if options.PlaylistStart > 0 { cmd.Args = append(cmd.Args, - "--playlist-start", strconv.Itoa(int(options.PlaylistStart)), + "--playlist-start", strconv.FormatUint(uint64(options.PlaylistStart), 10), ) } if options.PlaylistEnd > 0 { cmd.Args = append(cmd.Args, - "--playlist-end", strconv.Itoa(int(options.PlaylistEnd)), + "--playlist-end", strconv.FormatUint(uint64(options.PlaylistEnd), 10), ) } if options.FlatPlaylist { @@ -280,7 +280,7 @@ func infoFromURL( resp, respErr := get(info.Thumbnail) if respErr == nil { buf, _ := io.ReadAll(resp.Body) - resp.Body.Close() + _ = resp.Body.Close() info.ThumbnailBytes = buf } } @@ -297,7 +297,7 @@ func infoFromURL( resp, respErr := get(subtitle.URL) if respErr == nil { buf, _ := io.ReadAll(resp.Body) - resp.Body.Close() + _ = resp.Body.Close() subtitles[i].Bytes = buf } } diff --git a/internal/photoprism/dl/options.go b/internal/photoprism/dl/options.go index 32f0cbd1c..bfc0ac1d1 100644 --- a/internal/photoprism/dl/options.go +++ b/internal/photoprism/dl/options.go @@ -89,7 +89,7 @@ func (result Metadata) DownloadWithOptions( if !result.Options.noInfoDownload { jsonTempPath = path.Join(tempPath, "info.json") if err := os.WriteFile(jsonTempPath, result.RawJSON, 0600); err != nil { - os.RemoveAll(tempPath) + _ = os.RemoveAll(tempPath) return nil, err } } @@ -98,7 +98,7 @@ func (result Metadata) DownloadWithOptions( waitCh: make(chan struct{}), } - cmd := exec.CommandContext( + cmd := exec.CommandContext( // #nosec G204 yt-dlp command uses validated binary path and controlled args ctx, FindYtDlpBin(), // see comment below about ignoring errors for playlists @@ -130,12 +130,12 @@ func (result Metadata) DownloadWithOptions( if result.Options.PlaylistStart > 0 { cmd.Args = append(cmd.Args, - "--playlist-start", strconv.Itoa(int(result.Options.PlaylistStart)), + "--playlist-start", strconv.FormatUint(uint64(result.Options.PlaylistStart), 10), ) } if result.Options.PlaylistEnd > 0 { cmd.Args = append(cmd.Args, - "--playlist-end", strconv.Itoa(int(result.Options.PlaylistEnd)), + "--playlist-end", strconv.FormatUint(uint64(result.Options.PlaylistEnd), 10), ) } } else { @@ -268,15 +268,15 @@ func (result Metadata) DownloadWithOptions( log.Trace("cmd", " ", redactArgs(cmd.Args)) if err := cmd.Start(); err != nil { - os.RemoveAll(tempPath) + _ = os.RemoveAll(tempPath) return nil, err } go func() { _ = cmd.Wait() - stdoutW.Close() - stderrW.Close() - os.RemoveAll(tempPath) + _ = stdoutW.Close() + _ = stderrW.Close() + _ = os.RemoveAll(tempPath) close(dr.waitCh) }() diff --git a/internal/photoprism/dl/options_headers_test.go b/internal/photoprism/dl/options_headers_test.go index 7de663891..a74ffced1 100644 --- a/internal/photoprism/dl/options_headers_test.go +++ b/internal/photoprism/dl/options_headers_test.go @@ -39,6 +39,7 @@ func createFakeYtDlp(t *testing.T) string { script.WriteString("echo '[download]' 1>&2\n") script.WriteString("echo 'DATA'\n") } + // #nosec G306 executable test helper script needs exec permissions if err := os.WriteFile(path, script.Bytes(), 0o755); err != nil { t.Fatalf("failed to write fake yt-dlp: %v", err) } @@ -64,7 +65,7 @@ func TestInfoFromURL_IncludesHeadersAndCookies(t *testing.T) { t.Fatalf("infoFromURL error: %v", err) } - data, err := os.ReadFile(argsLog) + data, err := os.ReadFile(filepath.Clean(argsLog)) if err != nil { t.Fatalf("reading args log failed: %v", err) } @@ -102,7 +103,7 @@ func TestDownloadWithOptions_IncludesHeadersAndCookies_Pipe(t *testing.T) { _, _ = dr.Read(buf) _ = dr.Close() - data, err := os.ReadFile(argsLog) + data, err := os.ReadFile(filepath.Clean(argsLog)) if err != nil { t.Fatalf("reading args log failed: %v", err) } @@ -132,7 +133,7 @@ func TestDownloadWithOptions_OmitsFilterWhenDirect(t *testing.T) { if err != nil { t.Fatalf("DownloadWithOptions error: %v", err) } - data, err := os.ReadFile(argsLog) + data, err := os.ReadFile(filepath.Clean(argsLog)) if err != nil { t.Fatalf("reading args log failed: %v", err) } @@ -196,7 +197,7 @@ func TestDownloadToFileWithOptions_IncludesPostprocessorArgs(t *testing.T) { t.Fatalf("DownloadToFileWithOptions error: %v", err) } - data, err := os.ReadFile(argsLog) + data, err := os.ReadFile(filepath.Clean(argsLog)) if err != nil { t.Fatalf("reading args log failed: %v", err) } @@ -231,7 +232,7 @@ func TestDownloadWithOptions_IncludesPostprocessorArgs_Pipe(t *testing.T) { _, _ = dr.Read(buf) _ = dr.Close() - data, err := os.ReadFile(argsLog) + data, err := os.ReadFile(filepath.Clean(argsLog)) if err != nil { t.Fatalf("reading args log failed: %v", err) } diff --git a/internal/photoprism/dl/version.go b/internal/photoprism/dl/version.go index 59cc11381..c91fb935c 100644 --- a/internal/photoprism/dl/version.go +++ b/internal/photoprism/dl/version.go @@ -29,7 +29,7 @@ func VersionWarning() (string, bool) { return } - cmd := exec.Command(bin, "--version") + cmd := exec.Command(bin, "--version") // #nosec G204 yt-dlp path validated via FindYtDlpBin cmd.Env = os.Environ() var out bytes.Buffer cmd.Stdout = &out diff --git a/internal/photoprism/dl/version_test.go b/internal/photoprism/dl/version_test.go index b2ba65c8c..99a277436 100644 --- a/internal/photoprism/dl/version_test.go +++ b/internal/photoprism/dl/version_test.go @@ -50,6 +50,7 @@ func writeVersionScript(t *testing.T, version string) string { if runtime.GOOS == "windows" { content := "@echo off\r\n" + "echo " + version + "\r\n" + // #nosec G306 executable test helper script if err := os.WriteFile(path, []byte(content), 0o755); err != nil { t.Fatalf("failed to write fake yt-dlp: %v", err) } @@ -59,6 +60,7 @@ func writeVersionScript(t *testing.T, version string) string { content := "#!/usr/bin/env bash\n" + "set -euo pipefail\n" + "echo '" + version + "'\n" + // #nosec G306 executable test helper script if err := os.WriteFile(path, []byte(content), 0o755); err != nil { t.Fatalf("failed to write fake yt-dlp: %v", err) } From 6e0416db3449107c7d0b1038d9a288ac59309198 Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sat, 22 Nov 2025 10:22:52 +0100 Subject: [PATCH 013/195] Backend: Apply linter recommendations to "backup" package #5330 Signed-off-by: Michael Mayer --- internal/photoprism/backup/database.go | 29 ++++++++++++++------------ 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/internal/photoprism/backup/database.go b/internal/photoprism/backup/database.go index 21b8de003..a463d9db4 100644 --- a/internal/photoprism/backup/database.go +++ b/internal/photoprism/backup/database.go @@ -76,7 +76,7 @@ func Database(backupPath, fileName string, toStdOut, force bool, retain int) (er case config.MySQL, config.MariaDB: // Connect via Unix Domain Socket? if socketName := c.DatabaseServer(); strings.HasPrefix(socketName, "/") { - cmd = exec.Command( + cmd = exec.Command( // #nosec G204 database connection parameters from trusted config c.MariadbDumpBin(), "--protocol", "socket", "-S", socketName, @@ -88,7 +88,7 @@ func Database(backupPath, fileName string, toStdOut, force bool, retain int) (er // see https://mariadb.org/mission-impossible-zero-configuration-ssl/ log.Infof("backup: server supports zero-configuration ssl") - cmd = exec.Command( + cmd = exec.Command( // #nosec G204 database connection parameters from trusted config c.MariadbDumpBin(), "--protocol", "tcp", "-h", c.DatabaseHost(), @@ -101,7 +101,7 @@ func Database(backupPath, fileName string, toStdOut, force bool, retain int) (er // see https://mariadb.org/mission-impossible-zero-configuration-ssl/ log.Infof("backup: zero-configuration ssl not supported by the server") - cmd = exec.Command( + cmd = exec.Command( // #nosec G204 database connection parameters from trusted config c.MariadbDumpBin(), "--protocol", "tcp", "--skip-ssl", @@ -117,7 +117,7 @@ func Database(backupPath, fileName string, toStdOut, force bool, retain int) (er return fmt.Errorf("sqlite database file %s not found", clean.LogQuote(c.DatabaseFile())) } - cmd = exec.Command( + cmd = exec.Command( // #nosec G204 sqlite dump uses configured binary and db path c.SqliteBin(), c.DatabaseFile(), ".dump", @@ -131,6 +131,7 @@ func Database(backupPath, fileName string, toStdOut, force bool, retain int) (er if toStdOut { log.Infof("backup: sending database backup to stdout") f = os.Stdout + // #nosec G304 backup path validated by configuration } else if f, err = os.OpenFile(fileName, os.O_TRUNC|os.O_RDWR|os.O_CREATE, fs.ModeBackupFile); err != nil { return fmt.Errorf("failed to create %s (%s)", clean.Log(fileName), err) } else { @@ -239,11 +240,12 @@ func RestoreDatabase(backupPath, fileName string, fromStdIn, force bool) (err er Select("COUNT(*) AS photos"). Take(&counts) - if counts.Photos == 0 { - // Do nothing; - } else if !force { + switch { + case counts.Photos == 0: + // No existing data to guard against. + case !force: return fmt.Errorf("found an existing index with %d pictures, backup not restored", counts.Photos) - } else { + default: log.Warnf("restore: existing index with %d pictures will be replaced", counts.Photos) } @@ -255,7 +257,7 @@ func RestoreDatabase(backupPath, fileName string, fromStdIn, force bool) (err er case config.MySQL, config.MariaDB: // Connect via Unix Domain Socket? if socketName := c.DatabaseServer(); strings.HasPrefix(socketName, "/") { - cmd = exec.Command( + cmd = exec.Command( // #nosec G204 database connection parameters from config c.MariadbBin(), "--protocol", "socket", "-S", socketName, @@ -268,7 +270,7 @@ func RestoreDatabase(backupPath, fileName string, fromStdIn, force bool) (err er // see https://mariadb.org/mission-impossible-zero-configuration-ssl/ log.Infof("restore: server supports zero-configuration ssl") - cmd = exec.Command( + cmd = exec.Command( // #nosec G204 database connection parameters from config c.MariadbBin(), "--protocol", "tcp", "-h", c.DatabaseHost(), @@ -282,7 +284,7 @@ func RestoreDatabase(backupPath, fileName string, fromStdIn, force bool) (err er // see https://mariadb.org/mission-impossible-zero-configuration-ssl/ log.Infof("restore: zero-configuration ssl not supported by the server") - cmd = exec.Command( + cmd = exec.Command( // #nosec G204 database connection parameters from config c.MariadbBin(), "--protocol", "tcp", "--skip-ssl", @@ -297,7 +299,7 @@ func RestoreDatabase(backupPath, fileName string, fromStdIn, force bool) (err er case config.SQLite3: log.Infoln("restore: dropping existing sqlite database tables") tables.Drop(c.Db()) - cmd = exec.Command( + cmd = exec.Command( // #nosec G204 sqlite restore uses configured binary and db path c.SqliteBin(), c.DatabaseFile(), ) @@ -310,6 +312,7 @@ func RestoreDatabase(backupPath, fileName string, fromStdIn, force bool) (err er if fromStdIn { log.Infof("restore: restoring database backup from stdin") f = os.Stdin + // #nosec G304 backup path validated by configuration } else if f, err = os.OpenFile(fileName, os.O_RDONLY, 0); err != nil { return fmt.Errorf("failed to open %s: %s", clean.Log(fileName), err) } else { @@ -324,7 +327,7 @@ func RestoreDatabase(backupPath, fileName string, fromStdIn, force bool) (err er stdin, err = cmd.StdinPipe() if err != nil { - log.Fatal(err) + return fmt.Errorf("restore: failed to create stdin pipe: %w", err) } go func() { From cca86c08c74d3f0c49f5055cd8e02228b2b0f8cc Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sat, 22 Nov 2025 10:31:50 +0100 Subject: [PATCH 014/195] Batch: Perform priority comparison after resolving the PhotoLabel #271 Signed-off-by: Michael Mayer --- internal/photoprism/batch/apply_labels.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/internal/photoprism/batch/apply_labels.go b/internal/photoprism/batch/apply_labels.go index ba29aebca..651997cb2 100644 --- a/internal/photoprism/batch/apply_labels.go +++ b/internal/photoprism/batch/apply_labels.go @@ -110,8 +110,10 @@ func ApplyLabels(photo *entity.Photo, labels Items) (errs []error) { continue } labelIndex[labelEntity.ID] = pl - } else if entity.SrcPriority[pl.LabelSrc] > entity.SrcPriority[entity.SrcBatch] { - // Keep existing label with higher priority. + } + + // Keep existing label with higher priority than batch updates, even if it wasn't preloaded. + if entity.SrcPriority[pl.LabelSrc] > entity.SrcPriority[entity.SrcBatch] { continue } From 2002d39bfc5f370cf2b30e00550683a4387ede76 Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sat, 22 Nov 2025 10:51:35 +0100 Subject: [PATCH 015/195] Batch: Apply Go linter recommendations #271 #5330 Signed-off-by: Michael Mayer --- internal/photoprism/batch/action.go | 8 ++++++-- internal/photoprism/batch/photos.go | 8 ++++---- internal/photoprism/batch/photos_test.go | 2 +- internal/photoprism/batch/request.go | 7 +------ internal/photoprism/batch/safe.go | 9 +++++++++ internal/photoprism/batch/safe_test.go | 24 ++++++++++++++++++++++++ 6 files changed, 45 insertions(+), 13 deletions(-) create mode 100644 internal/photoprism/batch/safe.go create mode 100644 internal/photoprism/batch/safe_test.go diff --git a/internal/photoprism/batch/action.go b/internal/photoprism/batch/action.go index f245aeedf..afede4042 100644 --- a/internal/photoprism/batch/action.go +++ b/internal/photoprism/batch/action.go @@ -4,8 +4,12 @@ package batch type Action = string const ( - ActionNone Action = "none" + // ActionNone indicates that no change should be applied. + ActionNone Action = "none" + // ActionUpdate applies changes to existing values. ActionUpdate Action = "update" - ActionAdd Action = "add" + // ActionAdd adds a value to a collection (e.g., labels/albums). + ActionAdd Action = "add" + // ActionRemove removes a value from a collection (e.g., labels/albums). ActionRemove Action = "remove" ) diff --git a/internal/photoprism/batch/photos.go b/internal/photoprism/batch/photos.go index e235fcf5a..3a34bb392 100644 --- a/internal/photoprism/batch/photos.go +++ b/internal/photoprism/batch/photos.go @@ -352,17 +352,17 @@ func NewPhotosFormWithEntities(photos search.PhotoResults, preloaded map[string] } if i == 0 { - frm.CameraID.Value = int(photo.CameraID) + frm.CameraID.Value = int(photo.CameraID) //nolint:gosec // IDs bounded; ARMv7 deprecated frm.CameraID.Action = ActionNone - } else if photo.CameraID != uint(frm.CameraID.Value) { + } else if photo.CameraID != intToSafeUint(frm.CameraID.Value, entity.UnknownCamera.ID) { frm.CameraID.Mixed = true frm.CameraID.Value = -2 } if i == 0 { - frm.LensID.Value = int(photo.LensID) + frm.LensID.Value = int(photo.LensID) //nolint:gosec // IDs bounded; ARMv7 deprecated frm.LensID.Action = ActionNone - } else if photo.LensID != uint(frm.LensID.Value) { + } else if photo.LensID != intToSafeUint(frm.LensID.Value, entity.UnknownLens.ID) { frm.LensID.Mixed = true frm.LensID.Value = -2 } diff --git a/internal/photoprism/batch/photos_test.go b/internal/photoprism/batch/photos_test.go index abfd3db70..9a65bc774 100644 --- a/internal/photoprism/batch/photos_test.go +++ b/internal/photoprism/batch/photos_test.go @@ -25,7 +25,7 @@ func runNewPhotosFormFromJSON(t *testing.T) { var photos search.PhotoResults dataFile := fs.Abs("./testdata/photos.json") - data, err := os.ReadFile(dataFile) + data, err := os.ReadFile(dataFile) // #nosec G304 test fixture path is static if err != nil { t.Fatal(err) } diff --git a/internal/photoprism/batch/request.go b/internal/photoprism/batch/request.go index 895ca74d2..8fcd81fa0 100644 --- a/internal/photoprism/batch/request.go +++ b/internal/photoprism/batch/request.go @@ -11,12 +11,7 @@ type PhotosRequest struct { // Empty checks if any specific items were selected. func (f PhotosRequest) Empty() bool { - switch { - case len(f.Photos) > 0: - return false - } - - return true + return len(f.Photos) == 0 } // Get returns a string slice with the selected item UIDs. diff --git a/internal/photoprism/batch/safe.go b/internal/photoprism/batch/safe.go new file mode 100644 index 000000000..c753e8fa7 --- /dev/null +++ b/internal/photoprism/batch/safe.go @@ -0,0 +1,9 @@ +package batch + +// intToSafeUint casts an int to uint, returning the provided fallback when the value is negative. +func intToSafeUint(v int, fallback uint) uint { + if v < 0 { + return fallback + } + return uint(v) +} diff --git a/internal/photoprism/batch/safe_test.go b/internal/photoprism/batch/safe_test.go new file mode 100644 index 000000000..70b6626b3 --- /dev/null +++ b/internal/photoprism/batch/safe_test.go @@ -0,0 +1,24 @@ +package batch + +import "testing" + +func TestIntToSafeUint(t *testing.T) { + tests := []struct { + name string + value int + fallback uint + expect uint + }{ + {name: "Negative", value: -5, fallback: 42, expect: 42}, + {name: "Zero", value: 0, fallback: 7, expect: 0}, + {name: "Positive", value: 15, fallback: 9, expect: 15}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := intToSafeUint(tt.value, tt.fallback); got != tt.expect { + t.Fatalf("expected %d, got %d", tt.expect, got) + } + }) + } +} From 53da18754bb8f7df711100a7c9a7abc7cb13f1f6 Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sat, 22 Nov 2025 11:15:17 +0100 Subject: [PATCH 016/195] CI: Apply Go linter recommendations to "ai/vision" package #5330 Signed-off-by: Michael Mayer --- internal/ai/vision/api_format.go | 8 ++- internal/ai/vision/api_request.go | 2 + internal/ai/vision/api_response.go | 9 ++-- internal/ai/vision/config.go | 58 +++++++++++++-------- internal/ai/vision/config_test.go | 2 +- internal/ai/vision/engine_ollama.go | 3 +- internal/ai/vision/engine_openai.go | 6 ++- internal/ai/vision/errors.go | 1 + internal/ai/vision/model.go | 4 +- internal/ai/vision/model_run.go | 24 ++++++--- internal/ai/vision/model_test.go | 2 +- internal/ai/vision/model_types.go | 16 ++++-- internal/ai/vision/ollama/ollama.go | 6 --- internal/ai/vision/openai/openai.go | 6 --- internal/ai/vision/openai/transport_test.go | 2 +- internal/ai/vision/schema/name.go | 3 +- internal/ai/vision/vision.go | 1 + 17 files changed, 94 insertions(+), 59 deletions(-) diff --git a/internal/ai/vision/api_format.go b/internal/ai/vision/api_format.go index 5a68a8b56..a99446e5f 100644 --- a/internal/ai/vision/api_format.go +++ b/internal/ai/vision/api_format.go @@ -5,12 +5,18 @@ import ( "github.com/photoprism/photoprism/internal/ai/vision/openai" ) +// ApiFormat defines the payload format accepted by the Vision API. type ApiFormat = string const ( - ApiFormatUrl ApiFormat = "url" + // ApiFormatUrl treats inputs as HTTP(S) URLs. + ApiFormatUrl ApiFormat = "url" + // ApiFormatImages sends images in the native Vision format. ApiFormatImages ApiFormat = "images" + // ApiFormatVision represents a Vision-internal payload. ApiFormatVision ApiFormat = "vision" + // ApiFormatOllama proxies requests to Ollama models. ApiFormatOllama ApiFormat = ollama.ApiFormat + // ApiFormatOpenAI proxies requests to OpenAI vision models. ApiFormatOpenAI ApiFormat = openai.ApiFormat ) diff --git a/internal/ai/vision/api_request.go b/internal/ai/vision/api_request.go index f227b7a75..79e6d3e0d 100644 --- a/internal/ai/vision/api_request.go +++ b/internal/ai/vision/api_request.go @@ -21,9 +21,11 @@ import ( "github.com/photoprism/photoprism/pkg/rnd" ) +// Files holds a list of input file paths or URLs for vision requests. type Files = []string const ( + // FormatJSON indicates JSON payloads. FormatJSON = "json" logDataPreviewLength = 16 diff --git a/internal/ai/vision/api_response.go b/internal/ai/vision/api_response.go index a1a4c5536..364f56e7d 100644 --- a/internal/ai/vision/api_response.go +++ b/internal/ai/vision/api_response.go @@ -98,12 +98,13 @@ func (r LabelResult) ToClassify(labelSrc string) classify.Label { uncertainty = int(math.RoundToEven(float64(100 - r.Confidence*100))) } - // Default to "image" of no source name is provided. - if labelSrc != entity.SrcAuto { + // Default to "image" if no source name is provided. + switch { + case labelSrc != entity.SrcAuto: labelSrc = clean.ShortTypeLower(labelSrc) - } else if r.Source != "" { + case r.Source != "": labelSrc = clean.ShortTypeLower(r.Source) - } else { + default: labelSrc = entity.SrcImage } diff --git a/internal/ai/vision/config.go b/internal/ai/vision/config.go index 625dfb426..f62b7f265 100644 --- a/internal/ai/vision/config.go +++ b/internal/ai/vision/config.go @@ -16,22 +16,38 @@ import ( ) var ( - CachePath = "" - ModelsPath = "" - DownloadUrl = "" - ServiceApi = false - ServiceUri = "" - ServiceKey = "" - ServiceTimeout = 10 * time.Minute - ServiceMethod = http.MethodPost - ServiceFileScheme = scheme.Data - ServiceRequestFormat = ApiFormatVision + // CachePath stores the directory used for caching downloaded vision models. + CachePath = "" + // ModelsPath stores the directory containing downloaded vision models. + ModelsPath = "" + // DownloadUrl overrides the default model download endpoint when set. + DownloadUrl = "" + // ServiceApi enables exposing vision APIs via the service layer when true. + ServiceApi = false + // ServiceUri sets the base URI for the vision service when exposed externally. + ServiceUri = "" + // ServiceKey provides an optional API key for the vision service. + ServiceKey = "" + // ServiceTimeout sets the maximum duration for service API requests. + ServiceTimeout = 10 * time.Minute + // ServiceMethod defines the HTTP verb used when calling the vision service. + ServiceMethod = http.MethodPost + // ServiceFileScheme specifies how local files are encoded when sent to the service. + ServiceFileScheme = scheme.Data + // ServiceRequestFormat sets the default payload format for service requests. + ServiceRequestFormat = ApiFormatVision + // ServiceResponseFormat sets the expected response format from the service. ServiceResponseFormat = ApiFormatVision - DefaultResolution = 224 - DefaultTemperature = 0.1 - MaxTemperature = 2.0 - DefaultSrc = entity.SrcImage - DetectNSFWLabels = false + // DefaultResolution specifies the default square resize dimension for model inputs. + DefaultResolution = 224 + // DefaultTemperature sets the sampling temperature for compatible models. + DefaultTemperature = 0.1 + // MaxTemperature clamps user-supplied temperatures to a safe upper bound. + MaxTemperature = 2.0 + // DefaultSrc defines the fallback source string for generated labels. + DefaultSrc = entity.SrcImage + // DetectNSFWLabels toggles NSFW label detection in vision responses. + DetectNSFWLabels = false ) // Config reference the current configuration options. @@ -65,7 +81,7 @@ func (c *ConfigValues) Load(fileName string) error { return fmt.Errorf("%s not found", clean.Log(fileName)) } - yamlConfig, err := os.ReadFile(fileName) + yamlConfig, err := os.ReadFile(fileName) // #nosec G304 fileName is from validated config path if err != nil { return err @@ -141,11 +157,7 @@ func (c *ConfigValues) Save(fileName string) error { return err } - if err = os.WriteFile(fileName, data, fs.ModeConfigFile); err != nil { - return err - } - - return nil + return os.WriteFile(fileName, data, fs.ModeConfigFile) } // Model returns the first enabled model with the matching type. @@ -260,18 +272,22 @@ func GetModelsPath() string { return ModelsPath } +// GetModelPath returns the absolute path of a named model file in CachePath. func GetModelPath(name string) string { return filepath.Join(GetModelsPath(), clean.Path(clean.TypeLowerUnderscore(name))) } +// GetNasnetModelPath returns the absolute path of the default Nasnet model. func GetNasnetModelPath() string { return GetModelPath(NasnetModel.Name) } +// GetFacenetModelPath returns the absolute path of the default Facenet model. func GetFacenetModelPath() string { return GetModelPath(FacenetModel.Name) } +// GetNsfwModelPath returns the absolute path of the default NSFW model. func GetNsfwModelPath() string { return GetModelPath(NsfwModel.Name) } diff --git a/internal/ai/vision/config_test.go b/internal/ai/vision/config_test.go index d258aec29..32b9fb980 100644 --- a/internal/ai/vision/config_test.go +++ b/internal/ai/vision/config_test.go @@ -70,7 +70,7 @@ func TestConfigValues_LoadDefaultModelDisabled(t *testing.T) { } func TestConfigModelPrefersLastEnabled(t *testing.T) { - defaultModel := *NasnetModel + defaultModel := *NasnetModel //nolint:govet // copy for test to avoid mutating shared model defaultModel.Disabled = false defaultModel.Name = "nasnet-default" diff --git a/internal/ai/vision/engine_ollama.go b/internal/ai/vision/engine_ollama.go index 312321317..bb9db26de 100644 --- a/internal/ai/vision/engine_ollama.go +++ b/internal/ai/vision/engine_ollama.go @@ -70,8 +70,7 @@ func (ollamaDefaults) SchemaTemplate(model *Model) string { return "" } - switch model.Type { - case ModelTypeLabels: + if model.Type == ModelTypeLabels { return ollama.SchemaLabels(model.PromptContains("nsfw")) } diff --git a/internal/ai/vision/engine_openai.go b/internal/ai/vision/engine_openai.go index 3cb2a226e..be5ac5dcc 100644 --- a/internal/ai/vision/engine_openai.go +++ b/internal/ai/vision/engine_openai.go @@ -142,13 +142,15 @@ func (openaiBuilder) Build(ctx context.Context, model *Model, files Files) (*Api if opts := model.GetOptions(); opts != nil { req.Options = cloneOptions(opts) - if model.Type == ModelTypeCaption { + + switch model.Type { + case ModelTypeCaption: // Captions default to plain text responses; structured JSON is optional. req.Options.ForceJson = false if req.Options.MaxOutputTokens < openai.CaptionMaxTokens { req.Options.MaxOutputTokens = openai.CaptionMaxTokens } - } else if model.Type == ModelTypeLabels { + case ModelTypeLabels: if req.Options.MaxOutputTokens < openai.LabelsMaxTokens { req.Options.MaxOutputTokens = openai.LabelsMaxTokens } diff --git a/internal/ai/vision/errors.go b/internal/ai/vision/errors.go index 384fc3d14..9e24a24c9 100644 --- a/internal/ai/vision/errors.go +++ b/internal/ai/vision/errors.go @@ -5,5 +5,6 @@ import ( ) var ( + // ErrInvalidModel indicates an unknown or unsupported vision model name. ErrInvalidModel = fmt.Errorf("vision: invalid model") ) diff --git a/internal/ai/vision/model.go b/internal/ai/vision/model.go index 3672e76d3..0f28a2a21 100644 --- a/internal/ai/vision/model.go +++ b/internal/ai/vision/model.go @@ -488,6 +488,7 @@ func (m *Model) SchemaTemplate() string { if path == "" { path = envFile } + // #nosec G304 path comes from validated config/env if data, err := os.ReadFile(path); err != nil { log.Warnf("vision: failed to read schema from %s (%s)", clean.Log(path), err) } else { @@ -505,6 +506,7 @@ func (m *Model) SchemaTemplate() string { if path == "" { path = m.SchemaFile } + // #nosec G304 schema file path provided via config if data, err := os.ReadFile(path); err != nil { log.Warnf("vision: failed to read schema from %s (%s)", clean.Log(path), err) } else { @@ -757,6 +759,6 @@ func (m *Model) Clone() *Model { return nil } - c := *m + c := *m //nolint:govet // Model contains sync.Once; shallow copy used for reporting return &c } diff --git a/internal/ai/vision/model_run.go b/internal/ai/vision/model_run.go index 9d23c3175..b2fc3a8eb 100644 --- a/internal/ai/vision/model_run.go +++ b/internal/ai/vision/model_run.go @@ -8,14 +8,22 @@ import ( type RunType = string const ( - RunAuto RunType = "" // Automatically decide when to run based on model type and configuration. - RunNever RunType = "never" // Never run the model. - RunManual RunType = "manual" // Only run manually e.g. with the "vision run" command. - RunAlways RunType = "always" // Run manually, on-schedule, on-demand, and on-index. - RunNewlyIndexed RunType = "newly-indexed" // Run manually amd for newly-indexed pictures. - RunOnDemand RunType = "on-demand" // Run manually, for newly-indexed pictures, and on configured schedule. - RunOnSchedule RunType = "on-schedule" // Run manually and on-schedule. - RunOnIndex RunType = "on-index" // Run manually and on-index. + // RunAuto automatically decides when to run based on model type and configuration. + RunAuto RunType = "" + // RunNever disables the model entirely. + RunNever RunType = "never" + // RunManual runs only when explicitly invoked (e.g., via the "vision run" command). + RunManual RunType = "manual" + // RunAlways runs manually, on-schedule, on-demand, and on-index. + RunAlways RunType = "always" + // RunNewlyIndexed runs manually and for newly indexed pictures. + RunNewlyIndexed RunType = "newly-indexed" + // RunOnDemand runs manually, for newly indexed pictures, and on configured schedule. + RunOnDemand RunType = "on-demand" + // RunOnSchedule runs manually and on-schedule. + RunOnSchedule RunType = "on-schedule" + // RunOnIndex runs manually and after indexing. + RunOnIndex RunType = "on-index" ) // ReportRunType returns a human-readable string for the run type, preserving the diff --git a/internal/ai/vision/model_test.go b/internal/ai/vision/model_test.go index d8b258766..0728456a2 100644 --- a/internal/ai/vision/model_test.go +++ b/internal/ai/vision/model_test.go @@ -219,7 +219,7 @@ func TestModelGetSource(t *testing.T) { } func TestModel_IsDefault(t *testing.T) { - nasnetCopy := *NasnetModel + nasnetCopy := *NasnetModel //nolint:govet // copy for test inspection only nasnetCopy.Default = false cases := []struct { diff --git a/internal/ai/vision/model_types.go b/internal/ai/vision/model_types.go index a7fbcbd79..ccfb11863 100644 --- a/internal/ai/vision/model_types.go +++ b/internal/ai/vision/model_types.go @@ -5,14 +5,22 @@ import ( "strings" ) +// ModelType defines the classifier type used by a vision model (labels, caption, face, etc.). type ModelType = string + +// ModelTypes is a list of model type identifiers. type ModelTypes = []ModelType const ( - ModelTypeLabels ModelType = "labels" - ModelTypeNsfw ModelType = "nsfw" - ModelTypeFace ModelType = "face" - ModelTypeCaption ModelType = "caption" + // ModelTypeLabels runs label detection. + ModelTypeLabels ModelType = "labels" + // ModelTypeNsfw runs NSFW detection. + ModelTypeNsfw ModelType = "nsfw" + // ModelTypeFace performs face detection or recognition. + ModelTypeFace ModelType = "face" + // ModelTypeCaption generates captions. + ModelTypeCaption ModelType = "caption" + // ModelTypeGenerate produces new content (e.g., text-to-image), when supported. ModelTypeGenerate ModelType = "generate" ) diff --git a/internal/ai/vision/ollama/ollama.go b/internal/ai/vision/ollama/ollama.go index 6b906cc02..3bef6c5e3 100644 --- a/internal/ai/vision/ollama/ollama.go +++ b/internal/ai/vision/ollama/ollama.go @@ -24,9 +24,3 @@ Additional information can be found in our Developer Guide: */ package ollama - -import ( - "github.com/photoprism/photoprism/internal/event" -) - -var log = event.Log diff --git a/internal/ai/vision/openai/openai.go b/internal/ai/vision/openai/openai.go index de24ac98e..f3b8ce412 100644 --- a/internal/ai/vision/openai/openai.go +++ b/internal/ai/vision/openai/openai.go @@ -24,9 +24,3 @@ Additional information can be found in our Developer Guide: */ package openai - -import ( - "github.com/photoprism/photoprism/internal/event" -) - -var log = event.Log diff --git a/internal/ai/vision/openai/transport_test.go b/internal/ai/vision/openai/transport_test.go index 6141ea4f6..ac5b7a48d 100644 --- a/internal/ai/vision/openai/transport_test.go +++ b/internal/ai/vision/openai/transport_test.go @@ -12,7 +12,7 @@ func loadTestResponse(t *testing.T, name string) *Response { filePath := filepath.Join("testdata", name) - data, err := os.ReadFile(filePath) + data, err := os.ReadFile(filePath) // #nosec G304 test fixture path if err != nil { t.Fatalf("failed to read %s: %v", filePath, err) } diff --git a/internal/ai/vision/schema/name.go b/internal/ai/vision/schema/name.go index 2f642f2f3..6b4881dc6 100644 --- a/internal/ai/vision/schema/name.go +++ b/internal/ai/vision/schema/name.go @@ -9,6 +9,7 @@ import ( ) const ( + // NamePrefix prefixes generated schema identifiers for vision requests. NamePrefix = "photoprism_vision" ) @@ -19,7 +20,7 @@ func JsonSchemaName(schema json.RawMessage, version string) string { switch { case bytes.Contains(schema, []byte("labels")): schemaName = "labels" - case bytes.Contains(schema, []byte("labels")): + case bytes.Contains(schema, []byte("caption")): schemaName = "caption" default: schemaName = "schema" diff --git a/internal/ai/vision/vision.go b/internal/ai/vision/vision.go index 5e0f90b94..3461c9fdc 100644 --- a/internal/ai/vision/vision.go +++ b/internal/ai/vision/vision.go @@ -48,6 +48,7 @@ func ensureEnv() { } if path := strings.TrimSpace(os.Getenv("OPENAI_API_KEY_FILE")); fs.FileExistsNotEmpty(path) { + // #nosec G304 path provided via env if data, err := os.ReadFile(path); err == nil { if key := clean.Auth(string(data)); key != "" { _ = os.Setenv("OPENAI_API_KEY", key) From 4682791253770981cfa873bfbe40da54a6e426af Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sat, 22 Nov 2025 11:19:30 +0100 Subject: [PATCH 017/195] CI: Apply Go linter recommendations to "ai/classify" package #5330 Signed-off-by: Michael Mayer --- internal/ai/classify/label.go | 7 ++++--- internal/ai/classify/labels.go | 20 +++++++++++--------- internal/ai/classify/model_external_test.go | 6 +++--- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/internal/ai/classify/label.go b/internal/ai/classify/label.go index 97341ac5b..98d1bd5d0 100644 --- a/internal/ai/classify/label.go +++ b/internal/ai/classify/label.go @@ -53,11 +53,12 @@ func (l *Label) Title() string { // Confidence returns a matching confidence in percent. func (l *Label) Confidence() float32 { - if l.Uncertainty > 100 { + switch { + case l.Uncertainty > 100: return 0 - } else if l.Uncertainty < 0 { + case l.Uncertainty < 0: return 1 - } else { + default: return 1 - float32(l.Uncertainty)/100 } } diff --git a/internal/ai/classify/labels.go b/internal/ai/classify/labels.go index 86f290b2e..6ef3e29ef 100644 --- a/internal/ai/classify/labels.go +++ b/internal/ai/classify/labels.go @@ -20,13 +20,14 @@ func (l Labels) Swap(i, j int) { l[i], l[j] = l[j], l[i] } // for equal priority the lower-uncertainty label wins. Labels with an // uncertainty >= 100 are considered unusable and are ordered last. func (l Labels) Less(i, j int) bool { - if l[i].Uncertainty >= 100 { + switch { + case l[i].Uncertainty >= 100: return false - } else if l[j].Uncertainty >= 100 { + case l[j].Uncertainty >= 100: return true - } else if l[i].Priority == l[j].Priority { + case l[i].Priority == l[j].Priority: return l[i].Uncertainty < l[j].Uncertainty - } else { + default: return l[i].Priority > l[j].Priority } } @@ -139,15 +140,16 @@ func (l Labels) Title(fallback string) string { label = l[1] } - if fallback != "" && label.Priority < 0 { + switch { + case fallback != "" && label.Priority < 0: return fallback - } else if fallback != "" && label.Priority == 0 && label.Uncertainty > 50 { + case fallback != "" && label.Priority == 0 && label.Uncertainty > 50: return fallback - } else if label.Priority >= -1 && label.Uncertainty <= 60 { + case label.Priority >= -1 && label.Uncertainty <= 60: return label.Name + default: + return fallback } - - return fallback } // IsNSFW reports whether any label marks the asset as "not safe for work" diff --git a/internal/ai/classify/model_external_test.go b/internal/ai/classify/model_external_test.go index 35d4277e5..522d1be7a 100644 --- a/internal/ai/classify/model_external_test.go +++ b/internal/ai/classify/model_external_test.go @@ -25,7 +25,7 @@ const ( var baseUrl = "https://dl.photoprism.app/tensorflow/models" // To avoid downloading everything again and again... -//var baseUrl = "http://host.docker.internal:8000" +// var baseUrl = "http://host.docker.internal:8000" type ModelTestCase struct { Info *tensorflow.ModelInfo @@ -286,7 +286,7 @@ func testModel_LabelsFromFile(t *testing.T, tensorFlow *Model) { if len(result) > 0 { assert.Contains(t, result[0].Name, "chameleon") - //assert.Equal(t, 7, result[0].Uncertainty) + // assert.Equal(t, 7, result[0].Uncertainty) } }) t.Run(testName("cat_224.jpeg"), func(t *testing.T) { @@ -312,7 +312,7 @@ func testModel_LabelsFromFile(t *testing.T, tensorFlow *Model) { assert.NoError(t, err) assert.NotNil(t, result) assert.IsType(t, Labels{}, result) - //assert.Equal(t, 3, len(result)) + // assert.Equal(t, 3, len(result)) assert.GreaterOrEqual(t, len(result), 1) // t.Logf("labels: %#v", result) From 780a870f5cbecb5f84415bfdd2566f54eb86b70b Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sat, 22 Nov 2025 11:30:58 +0100 Subject: [PATCH 018/195] CI: Apply Go more linter recommendations to "ai/classify" package #5330 Signed-off-by: Michael Mayer --- internal/ai/classify/gen.go | 2 +- internal/ai/classify/model.go | 14 ++-- internal/ai/classify/model_external_test.go | 88 +++++++++++++++------ internal/ai/classify/model_test.go | 14 ++-- internal/ai/classify/rules.go | 1 + 5 files changed, 83 insertions(+), 36 deletions(-) diff --git a/internal/ai/classify/gen.go b/internal/ai/classify/gen.go index f8546cfad..c52366477 100644 --- a/internal/ai/classify/gen.go +++ b/internal/ai/classify/gen.go @@ -1,5 +1,4 @@ //go:build ignore -// +build ignore // This generates stopwords.go by running "go generate" package main @@ -88,6 +87,7 @@ package classify // Generated code, do not edit. +// Rules contains the generated label classification rules from rules.yml. var Rules = LabelRules{ {{- range $key, $value := .Rules }} {{ printf "%q" $key }}: { diff --git a/internal/ai/classify/model.go b/internal/ai/classify/model.go index fe54ee538..2cc242ed1 100644 --- a/internal/ai/classify/model.go +++ b/internal/ai/classify/model.go @@ -78,7 +78,7 @@ func NewNasnet(modelsPath string, disabled bool) *Model { }, disabled) } -// Init initialises tensorflow models if not disabled +// Init initializes tensorflow models if not disabled. func (m *Model) Init() (err error) { if m.disabled { return nil @@ -95,7 +95,7 @@ func (m *Model) File(fileName string, confidenceThreshold int) (result Labels, e var data []byte - if data, err = os.ReadFile(fileName); err != nil { + if data, err = os.ReadFile(fileName); err != nil { //nolint:gosec // fileName is provided by trusted callers; reading arbitrary local files is expected behavior return nil, err } @@ -203,13 +203,17 @@ func (m *Model) loadModel() (err error) { if len(m.meta.Tags) == 0 { infos, modelErr := tensorflow.GetModelTagsInfo(modelPath) - if modelErr != nil { + + switch { + case modelErr != nil: log.Errorf("classify: could not get info from model in %s (%s)", clean.Log(modelPath), clean.Error(modelErr)) - } else if len(infos) == 1 { + case len(infos) == 1: log.Debugf("classify: model info: %+v", infos[0]) m.meta.Merge(&infos[0]) - } else { + case len(infos) > 1: log.Warnf("classify: found %d metagraphs, which is too many", len(infos)) + default: + log.Warnf("classify: no metagraphs found in %s", clean.Log(modelPath)) } } diff --git a/internal/ai/classify/model_external_test.go b/internal/ai/classify/model_external_test.go index 522d1be7a..2cef51370 100644 --- a/internal/ai/classify/model_external_test.go +++ b/internal/ai/classify/model_external_test.go @@ -15,11 +15,13 @@ import ( "github.com/stretchr/testify/assert" "github.com/photoprism/photoprism/internal/ai/tensorflow" + "github.com/photoprism/photoprism/pkg/fs" ) const ( DefaultResolution = 224 ExternalModelsTestLabel = "PHOTOPRISM_TEST_EXTERNAL_MODELS" + maxArchiveFileSize = 2 * 1024 * 1024 * 1024 // 2 GiB limit to avoid decompression bombs in tests ) var baseUrl = "https://dl.photoprism.app/tensorflow/models" @@ -111,16 +113,43 @@ var modelsInfo = map[string]*ModelTestCase{ */ } -func isSafePath(target, baseDir string) bool { +func safeArchivePath(baseDir, name string) (string, error) { + cleanName := filepath.Clean(name) - // Resolve the absolute path of the target - absTarget := filepath.Join(baseDir, target) - absBase, err := filepath.Abs(baseDir) - if err != nil { - return false + if cleanName == "" || cleanName == "." { + return "", fmt.Errorf("empty archive path") } - return strings.HasPrefix(absTarget, absBase) + if filepath.IsAbs(cleanName) || filepath.VolumeName(cleanName) != "" { + return "", fmt.Errorf("absolute paths are not allowed") + } + + if cleanName == ".." || strings.HasPrefix(cleanName, ".."+string(os.PathSeparator)) { + return "", fmt.Errorf("path traversal detected") + } + + target := filepath.Join(baseDir, cleanName) //nolint:gosec // target is validated below + + absBase, err := filepath.Abs(baseDir) + if err != nil { + return "", err + } + + absTarget, err := filepath.Abs(target) + if err != nil { + return "", err + } + + rel, err := filepath.Rel(absBase, absTarget) + if err != nil { + return "", err + } + + if rel == ".." || strings.HasPrefix(rel, ".."+string(os.PathSeparator)) { + return "", fmt.Errorf("path escapes base directory") + } + + return absTarget, nil } func TestExternalModel_AllModels(t *testing.T) { @@ -159,20 +188,20 @@ func TestExternalModel_AllModels(t *testing.T) { model.meta.Input.SetResolution(DefaultResolution) } - testModel_LabelsFromFile(t, model) - testModel_Run(t, model) + testModelLabelsFromFile(t, model) + testModelRun(t, model) }) } } func downloadLabels(t *testing.T, url, dst string) { - resp, err := http.Get(url) + resp, err := http.Get(url) //nolint:gosec // test downloads from trusted PhotoPrism asset host if err != nil { t.Fatal(err) } defer resp.Body.Close() - output, err := os.Create(filepath.Join(dst, "labels.txt")) + output, err := os.Create(filepath.Join(dst, "labels.txt")) //nolint:gosec // destination is within a controlled temporary test directory if err != nil { t.Fatal(err) } @@ -189,9 +218,11 @@ func downloadRemoteModel(t *testing.T, url, tmpPath string) (model string) { modelPath := strings.TrimSuffix(path.Base(url), ".tar.gz") tmpPath = filepath.Join(tmpPath, modelPath) - os.MkdirAll(tmpPath, 0755) + if err := os.MkdirAll(tmpPath, fs.ModeDir); err != nil { //nolint:gosec // fs.ModeDir is the project default for directories + t.Fatal(err) + } - resp, err := http.Get(url) + resp, err := http.Get(url) //nolint:gosec // test downloads from trusted PhotoPrism asset host if err != nil { t.Fatal(err) } @@ -207,7 +238,7 @@ func downloadRemoteModel(t *testing.T, url, tmpPath string) (model string) { } tarReader := tar.NewReader(uncompressedBody) - for true { + for { header, err := tarReader.Next() if err == io.EOF { break @@ -217,30 +248,41 @@ func downloadRemoteModel(t *testing.T, url, tmpPath string) (model string) { t.Fatalf("could not extract the file: %v", err) } - target := filepath.Join(tmpPath, header.Name) - if !isSafePath(target, tmpPath) { - t.Fatalf("The model file contains an invalid path: %s", header.Name) + if strings.HasPrefix(header.Name, "__MACOSX") { + continue + } + + target, err := safeArchivePath(tmpPath, header.Name) + if err != nil { + t.Fatalf("The model file contains an invalid path %s: %v", header.Name, err) } switch header.Typeflag { case tar.TypeDir: - if err := os.Mkdir(target, 0755); err != nil { + if err := os.Mkdir(target, fs.ModeDir); err != nil { //nolint:gosec // fs.ModeDir is intentional for extracted model directories t.Fatalf("could not make the dir %s: %v", header.Name, err) } case tar.TypeReg: - outFile, err := os.Create(target) + outFile, err := os.Create(target) //nolint:gosec // target path validated by isSafePath and confined to tmpPath if err != nil { t.Fatalf("could not create file %s: %v", header.Name, err) } - if _, err := io.Copy(outFile, tarReader); err != nil { + limitedReader := &io.LimitedReader{R: tarReader, N: maxArchiveFileSize} + + if _, err := io.Copy(outFile, limitedReader); err != nil { t.Fatalf("could not copy file %s: %v", header.Name, err) } + if limitedReader.N == 0 { + t.Fatalf("file %s exceeds maximum allowed size of %d bytes", header.Name, maxArchiveFileSize) + } rootPath, fileName := filepath.Split(header.Name) if fileName == "saved_model.pb" { model = filepath.Join(modelPath, rootPath) } - outFile.Close() + if err := outFile.Close(); err != nil { + t.Fatalf("could not close file %s: %v", header.Name, err) + } default: t.Fatalf("could not extract file. Unknown type %v in %s", header.Typeflag, @@ -266,7 +308,7 @@ func assertContainsAny(t *testing.T, s string, substrings []string) { s, substrings) } -func testModel_LabelsFromFile(t *testing.T, tensorFlow *Model) { +func testModelLabelsFromFile(t *testing.T, tensorFlow *Model) { testName := func(name string) string { return fmt.Sprintf("%s/%s", tensorFlow.name, name) } @@ -367,7 +409,7 @@ func testModel_LabelsFromFile(t *testing.T, tensorFlow *Model) { }) } -func testModel_Run(t *testing.T, tensorFlow *Model) { +func testModelRun(t *testing.T, tensorFlow *Model) { if testing.Short() { t.Skip("skipping test in short mode.") } diff --git a/internal/ai/classify/model_test.go b/internal/ai/classify/model_test.go index 6779b38fa..ba63d7c08 100644 --- a/internal/ai/classify/model_test.go +++ b/internal/ai/classify/model_test.go @@ -181,7 +181,7 @@ func TestModel_Run(t *testing.T) { t.Run("ChameleonLimeJpg", func(t *testing.T) { tensorFlow := NewModelTest(t) - if imageBuffer, err := os.ReadFile(examplesPath + "/chameleon_lime.jpg"); err != nil { + if imageBuffer, err := os.ReadFile(filepath.Join(examplesPath, "chameleon_lime.jpg")); err != nil { //nolint:gosec // reading bundled test fixture t.Error(err) } else { result, err := tensorFlow.Run(imageBuffer, 10) @@ -206,7 +206,7 @@ func TestModel_Run(t *testing.T) { t.Run("DogOrangeJpg", func(t *testing.T) { tensorFlow := NewModelTest(t) - if imageBuffer, err := os.ReadFile(examplesPath + "/dog_orange.jpg"); err != nil { + if imageBuffer, err := os.ReadFile(filepath.Join(examplesPath, "dog_orange.jpg")); err != nil { //nolint:gosec // reading bundled test fixture t.Error(err) } else { result, err := tensorFlow.Run(imageBuffer, 10) @@ -231,7 +231,7 @@ func TestModel_Run(t *testing.T) { t.Run("RandomDocx", func(t *testing.T) { tensorFlow := NewModelTest(t) - if imageBuffer, err := os.ReadFile(examplesPath + "/Random.docx"); err != nil { + if imageBuffer, err := os.ReadFile(filepath.Join(examplesPath, "Random.docx")); err != nil { //nolint:gosec // reading bundled test fixture t.Error(err) } else { result, err := tensorFlow.Run(imageBuffer, 10) @@ -242,7 +242,7 @@ func TestModel_Run(t *testing.T) { t.Run("Num6720PxWhiteJpg", func(t *testing.T) { tensorFlow := NewModelTest(t) - if imageBuffer, err := os.ReadFile(examplesPath + "/6720px_white.jpg"); err != nil { + if imageBuffer, err := os.ReadFile(filepath.Join(examplesPath, "6720px_white.jpg")); err != nil { //nolint:gosec // reading bundled test fixture t.Error(err) } else { result, err := tensorFlow.Run(imageBuffer, 10) @@ -257,7 +257,7 @@ func TestModel_Run(t *testing.T) { t.Run("Disabled", func(t *testing.T) { tensorFlow := NewNasnet(modelsPath, true) - if imageBuffer, err := os.ReadFile(examplesPath + "/dog_orange.jpg"); err != nil { + if imageBuffer, err := os.ReadFile(filepath.Join(examplesPath, "dog_orange.jpg")); err != nil { //nolint:gosec // reading bundled test fixture t.Error(err) } else { result, err := tensorFlow.Run(imageBuffer, 10) @@ -328,7 +328,7 @@ func BenchmarkModel_BestLabelWithOptimization(b *testing.B) { b.Fatal(err) } - imageBuffer, err := os.ReadFile(examplesPath + "/dog_orange.jpg") + imageBuffer, err := os.ReadFile(filepath.Join(examplesPath, "dog_orange.jpg")) //nolint:gosec // reading bundled test fixture if err != nil { b.Fatal(err) } @@ -349,7 +349,7 @@ func BenchmarkModel_BestLabelsNoOptimization(b *testing.B) { } model.builder = nil - imageBuffer, err := os.ReadFile(examplesPath + "/dog_orange.jpg") + imageBuffer, err := os.ReadFile(filepath.Join(examplesPath, "dog_orange.jpg")) //nolint:gosec // reading bundled test fixture if err != nil { b.Fatal(err) } diff --git a/internal/ai/classify/rules.go b/internal/ai/classify/rules.go index 953f2dee9..58863333d 100644 --- a/internal/ai/classify/rules.go +++ b/internal/ai/classify/rules.go @@ -2,6 +2,7 @@ package classify // Generated code, do not edit. +// Rules contains the generated label classification rules from rules.yml. var Rules = LabelRules{ "abacus": { Label: "", From 59c8754ca3bb17365424f49ce5bea114725a532c Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sat, 22 Nov 2025 11:33:28 +0100 Subject: [PATCH 019/195] CI: Apply Go more linter recommendations to "ai/nsfw" package #5330 Signed-off-by: Michael Mayer --- internal/ai/nsfw/model.go | 16 ++++++++++------ internal/ai/nsfw/nsfw.go | 2 ++ internal/ai/nsfw/nsfw_test.go | 2 +- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/internal/ai/nsfw/model.go b/internal/ai/nsfw/model.go index 54a910252..19d1af5ce 100644 --- a/internal/ai/nsfw/model.go +++ b/internal/ai/nsfw/model.go @@ -47,7 +47,7 @@ func (m *Model) File(fileName string) (result Result, err error) { var img []byte - if img, err = os.ReadFile(fileName); err != nil { + if img, err = os.ReadFile(fileName); err != nil { //nolint:gosec // fileName is provided by trusted callers; reading local test fixtures is intentional return result, err } @@ -109,7 +109,7 @@ func (m *Model) Run(img []byte) (result Result, err error) { return result, nil } -// Init initialises tensorflow models if not disabled +// Init initializes tensorflow models if not disabled. func (m *Model) Init() (err error) { if m.disabled { return nil @@ -133,13 +133,17 @@ func (m *Model) loadModel() error { if len(m.meta.Tags) == 0 { infos, err := tensorflow.GetModelTagsInfo(m.modelPath) - if err != nil { + + switch { + case err != nil: log.Errorf("nsfw: could not get the model info at %s: %v", clean.Log(m.modelPath)) - } else if len(infos) == 1 { + case len(infos) == 1: log.Debugf("nsfw: model info: %+v", infos[0]) m.meta.Merge(&infos[0]) - } else { + case len(infos) > 1: log.Warnf("nsfw: found %d metagraphs... that's too many", len(infos)) + default: + log.Warnf("nsfw: no metagraphs found in %s", clean.Log(m.modelPath)) } } @@ -179,7 +183,7 @@ func (m *Model) loadModel() error { func (m *Model) loadLabels(modelPath string) (err error) { m.labels, err = tensorflow.LoadLabels(modelPath, int(m.meta.Output.NumOutputs)) - return nil + return err } func (m *Model) getLabels(p []float32) Result { diff --git a/internal/ai/nsfw/nsfw.go b/internal/ai/nsfw/nsfw.go index 9853b5635..7b7d9f3b1 100644 --- a/internal/ai/nsfw/nsfw.go +++ b/internal/ai/nsfw/nsfw.go @@ -28,6 +28,7 @@ import ( "github.com/photoprism/photoprism/internal/event" ) +// Thresholds for classifying NSFW scores. const ( ThresholdSafe = 0.75 ThresholdMedium = 0.85 @@ -36,6 +37,7 @@ const ( var log = event.Log +// Result represents the classification scores returned by the NSFW model. type Result struct { Drawing float32 Hentai float32 diff --git a/internal/ai/nsfw/nsfw_test.go b/internal/ai/nsfw/nsfw_test.go index 1edb6bb45..9f39fc336 100644 --- a/internal/ai/nsfw/nsfw_test.go +++ b/internal/ai/nsfw/nsfw_test.go @@ -84,7 +84,7 @@ func TestIsSafe(t *testing.T) { assert.GreaterOrEqual(t, l.Sexy, e.Sexy) } - isSafe := !(strings.Contains(basename, "porn") || strings.Contains(basename, "hentai")) + isSafe := !strings.Contains(basename, "porn") && !strings.Contains(basename, "hentai") if isSafe { assert.True(t, l.IsSafe()) From b954de52e9ba4c55a361a509ab1e7b4c184dc848 Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sat, 22 Nov 2025 11:38:45 +0100 Subject: [PATCH 020/195] CI: Apply Go more linter recommendations to "ai/face" package #5330 Signed-off-by: Michael Mayer --- internal/ai/face/detector.go | 15 ++++++++------- internal/ai/face/embedding.go | 1 + internal/ai/face/embeddings_random.go | 11 ++++++++--- internal/ai/face/engine.go | 5 +++++ internal/ai/face/engine_onnx.go | 13 +++++++------ internal/ai/face/model.go | 6 +++++- internal/ai/face/model_test.go | 4 ++-- 7 files changed, 36 insertions(+), 19 deletions(-) diff --git a/internal/ai/face/detector.go b/internal/ai/face/detector.go index bffb17e5a..c34545bbe 100644 --- a/internal/ai/face/detector.go +++ b/internal/ai/face/detector.go @@ -3,7 +3,7 @@ package face import ( _ "embed" "fmt" - _ "image/jpeg" + _ "image/jpeg" // register JPEG decoder for pigo image decoding "os" "path/filepath" "runtime/debug" @@ -146,7 +146,7 @@ func (d *pigoDetector) Detect(fileName string) (faces []pigo.Detection, params p d.angles = append([]float64(nil), DetectionAngles...) } - file, err := os.Open(fileName) + file, err := os.Open(fileName) //nolint:gosec // fileName comes from caller; reading local images is expected if err != nil { return faces, params, err @@ -169,11 +169,12 @@ func (d *pigoDetector) Detect(fileName string) (faces []pigo.Detection, params p var maxSize int - if cols < 20 || rows < 20 || cols < d.minSize || rows < d.minSize { + switch { + case cols < 20 || rows < 20 || cols < d.minSize || rows < d.minSize: return faces, params, fmt.Errorf("image size %dx%d is too small", cols, rows) - } else if cols < rows { + case cols < rows: maxSize = cols - 4 - } else { + default: maxSize = rows - 4 } @@ -370,8 +371,8 @@ func (d *pigoDetector) Faces(det []pigo.Detection, params pigo.CascadeParams, fi // Create face. f := Face{ - Rows: params.ImageParams.Rows, - Cols: params.ImageParams.Cols, + Rows: params.Rows, + Cols: params.Cols, Score: int(face.Q), Area: faceCoord, Eyes: eyesCoords, diff --git a/internal/ai/face/embedding.go b/internal/ai/face/embedding.go index 9aa85d9d4..5230061a8 100644 --- a/internal/ai/face/embedding.go +++ b/internal/ai/face/embedding.go @@ -10,6 +10,7 @@ import ( // Embedding represents a face embedding. type Embedding []float64 +// NullEmbedding is a zero-value placeholder embedding used when no data is available. var NullEmbedding = make(Embedding, 512) // NewEmbedding creates a new embedding from an inference result. diff --git a/internal/ai/face/embeddings_random.go b/internal/ai/face/embeddings_random.go index 09c427947..51f5ada3b 100644 --- a/internal/ai/face/embeddings_random.go +++ b/internal/ai/face/embeddings_random.go @@ -4,12 +4,17 @@ import ( "math/rand/v2" ) +// Kind identifies the type of embedding. type Kind int const ( + // RegularFace represents a standard face embedding. RegularFace Kind = iota + 1 + // ChildrenFace represents a child face embedding. ChildrenFace + // BackgroundFace represents non-face/background embeddings. BackgroundFace + // AmbiguousFace represents embeddings that should be treated as uncertain. AmbiguousFace ) @@ -20,7 +25,7 @@ func RandomDist() float64 { // RandomFloat64 adds a random distance offset to a float64. func RandomFloat64(f, d float64) float64 { - return f + (rand.Float64()-0.5)*d + return f + (rand.Float64()-0.5)*d //nolint:gosec // pseudo-random is sufficient for test fixtures } // RandomEmbeddings returns random embeddings for testing. @@ -76,7 +81,7 @@ func RandomChildrenEmbedding() (result Embedding) { } d := 0.1 / 512.0 - n := rand.IntN(len(Children)) + n := rand.IntN(len(Children)) //nolint:gosec // deterministic seeding not required for synthetic embeddings e := Children[n].Embedding for i := range result { @@ -97,7 +102,7 @@ func RandomBackgroundEmbedding() (result Embedding) { } d := 0.1 / 512.0 - n := rand.IntN(len(Background)) + n := rand.IntN(len(Background)) //nolint:gosec // deterministic seeding not required for synthetic embeddings e := Background[n].Embedding for i := range result { diff --git a/internal/ai/face/engine.go b/internal/ai/face/engine.go index f3d2bf4db..316465ba2 100644 --- a/internal/ai/face/engine.go +++ b/internal/ai/face/engine.go @@ -7,12 +7,17 @@ import ( "sync" ) +// EngineName identifies a face detection engine implementation. type EngineName = string const ( + // EngineAuto selects the default engine based on availability. EngineAuto EngineName = "auto" + // EnginePigo enables the built-in Pigo cascade detector. EnginePigo EngineName = "pigo" + // EngineONNX enables the ONNX runtime-powered SCRFD detector. EngineONNX EngineName = "onnx" + // EngineNone disables face detection. EngineNone EngineName = "none" ) diff --git a/internal/ai/face/engine_onnx.go b/internal/ai/face/engine_onnx.go index 28dcf3ecb..39c8f2434 100644 --- a/internal/ai/face/engine_onnx.go +++ b/internal/ai/face/engine_onnx.go @@ -4,7 +4,7 @@ import ( "errors" "fmt" "image" - _ "image/jpeg" + _ "image/jpeg" // register JPEG decoder for ONNX engine input "math" "os" "path/filepath" @@ -17,7 +17,7 @@ import ( onnxruntime "github.com/yalue/onnxruntime_go" ) -// ONNXOptions configures how the ONNX runtime-backed detector is initialised. +// ONNXOptions configures how the ONNX runtime-backed detector is initialized. type ONNXOptions struct { ModelPath string LibraryPath string @@ -27,6 +27,7 @@ type ONNXOptions struct { } const ( + // DefaultONNXModelFilename is the bundled ONNX model name used when none is provided. DefaultONNXModelFilename = "scrfd.onnx" onnxDefaultScoreThreshold = 0.50 onnxDefaultNMSThreshold = 0.40 @@ -307,7 +308,7 @@ func (o *onnxEngine) Close() error { // Detect identifies faces in the provided image using the ONNX runtime session. func (o *onnxEngine) Detect(fileName string, findLandmarks bool, minSize int) (Faces, error) { - file, err := os.Open(fileName) + file, err := os.Open(fileName) //nolint:gosec // fileName provided by caller; reading local images is required for detection if err != nil { return Faces{}, err } @@ -437,9 +438,9 @@ func (o *onnxEngine) buildBlob(img image.Image) ([]float32, float32, error) { var r, g, b float32 if x < newWidth && y < newHeight { cr, cg, cb, _ := resized.At(x, y).RGBA() - r = float32(uint8(cr >> 8)) - g = float32(uint8(cg >> 8)) - b = float32(uint8(cb >> 8)) + r = float32((cr >> 8) & 0xff) + g = float32((cg >> 8) & 0xff) + b = float32((cb >> 8) & 0xff) } blob[idx] = (r - onnxInputMean) / onnxInputStd diff --git a/internal/ai/face/model.go b/internal/ai/face/model.go index 951c5ecc2..59591ed80 100644 --- a/internal/ai/face/model.go +++ b/internal/ai/face/model.go @@ -86,7 +86,7 @@ func (m *Model) Detect(fileName string, minSize int, cacheCrop bool, expected in return faces, nil } -// Init initialises tensorflow models if not disabled +// Init initializes tensorflow models if not disabled. func (m *Model) Init() (err error) { if m.disabled { return nil @@ -139,6 +139,10 @@ func (m *Model) Run(img image.Image) Embeddings { // TODO: pre-whiten image as in facenet trainPhaseBoolTensor, err := tf.NewTensor(false) + if err != nil { + log.Errorf("faces: failed to create phase_train tensor: %s", err) + return nil + } output, err := m.model.Session.Run( map[tf.Output]*tf.Tensor{ diff --git a/internal/ai/face/model_test.go b/internal/ai/face/model_test.go index 523e5517b..069781503 100644 --- a/internal/ai/face/model_test.go +++ b/internal/ai/face/model_test.go @@ -123,11 +123,11 @@ func TestNet(t *testing.T) { t.Logf("Dist for %d %d (faces are %d %d) is %f", i, j, faceIndexToPersonID[i], faceIndexToPersonID[j], dist) if faceIndexToPersonID[i] == faceIndexToPersonID[j] { if dist < 1.21 { - correct += 1 + correct++ } } else { if dist >= 1.21 { - correct += 1 + correct++ } } } From 75bc6d754c37f28bb9c5cf6faa0aa343cb8cc9d8 Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sat, 22 Nov 2025 11:47:17 +0100 Subject: [PATCH 021/195] CI: Apply Go linter recommendations to "ai/tensorflow" package #5330 Signed-off-by: Michael Mayer --- internal/ai/tensorflow/image.go | 27 +++++++--- internal/ai/tensorflow/image_test.go | 4 +- internal/ai/tensorflow/info.go | 66 +++++++++++++++++------- internal/ai/tensorflow/info_test.go | 7 +-- internal/ai/tensorflow/labels.go | 2 +- internal/ai/tensorflow/model.go | 18 ++++--- internal/ai/tensorflow/model_test.go | 39 +++++++------- internal/ai/tensorflow/op.go | 1 + internal/ai/tensorflow/tensor_builder.go | 4 ++ internal/ai/tensorflow/util.go | 4 +- 10 files changed, 113 insertions(+), 59 deletions(-) diff --git a/internal/ai/tensorflow/image.go b/internal/ai/tensorflow/image.go index 3aea2d186..196b78d61 100644 --- a/internal/ai/tensorflow/image.go +++ b/internal/ai/tensorflow/image.go @@ -4,8 +4,9 @@ import ( "bytes" "fmt" "image" - _ "image/jpeg" - _ "image/png" + _ "image/jpeg" // register JPEG decoder + _ "image/png" // register PNG decoder + "math" "os" "runtime/debug" @@ -16,10 +17,13 @@ import ( ) const ( - Mean = float32(117) + // Mean is the default mean pixel value used during normalization. + Mean = float32(117) + // Scale is the default scale applied during normalization. Scale = float32(1) ) +// ImageFromFile decodes an image from disk and converts it to a tensor for inference. func ImageFromFile(fileName string, input *PhotoInput) (*tf.Tensor, error) { if img, err := OpenImage(fileName); err != nil { return nil, err @@ -28,8 +32,9 @@ func ImageFromFile(fileName string, input *PhotoInput) (*tf.Tensor, error) { } } +// OpenImage opens an image file and decodes it using the registered decoders. func OpenImage(fileName string) (image.Image, error) { - f, err := os.Open(fileName) + f, err := os.Open(fileName) //nolint:gosec // fileName supplied by trusted caller; reading local images is expected if err != nil { return nil, err } @@ -39,6 +44,7 @@ func OpenImage(fileName string) (image.Image, error) { return img, err } +// ImageFromBytes converts raw image bytes into a tensor using the provided input definition. func ImageFromBytes(b []byte, input *PhotoInput, builder *ImageTensorBuilder) (*tf.Tensor, error) { img, _, imgErr := image.Decode(bytes.NewReader(b)) @@ -49,6 +55,7 @@ func ImageFromBytes(b []byte, input *PhotoInput, builder *ImageTensorBuilder) (* return Image(img, input, builder) } +// Image converts a decoded image into a tensor matching the provided input description. func Image(img image.Image, input *PhotoInput, builder *ImageTensorBuilder) (tfTensor *tf.Tensor, err error) { defer func() { if r := recover(); r != nil { @@ -70,8 +77,8 @@ func Image(img image.Image, input *PhotoInput, builder *ImageTensorBuilder) (tfT for i := 0; i < input.Resolution(); i++ { for j := 0; j < input.Resolution(); j++ { r, g, b, _ := img.At(i, j).RGBA() - //Although RGB can be disordered, we assume the input intervals are - //given in RGB order. + // Although RGB can be disordered, we assume the input intervals are + // given in RGB order. builder.Set(i, j, convertValue(r, input.GetInterval(0)), convertValue(g, input.GetInterval(1)), @@ -116,6 +123,10 @@ func transformImageGraph(imageFormat fs.Type, resolution int) (graph *tf.Graph, s := op.NewScope() input = op.Placeholder(s, tf.String) + if resolution <= 0 || resolution > math.MaxInt32 { + return nil, input, output, fmt.Errorf("tensorflow: resolution %d is out of bounds", resolution) + } + // Assume the image is a JPEG, or a PNG if explicitly specified. var decodedImage tf.Output switch imageFormat { @@ -125,13 +136,15 @@ func transformImageGraph(imageFormat fs.Type, resolution int) (graph *tf.Graph, decodedImage = op.DecodeJpeg(s, input, op.DecodeJpegChannels(3)) } + size := int32(resolution) //nolint:gosec // resolution is validated to be within int32 range above + output = op.Div(s, op.Sub(s, op.ResizeBilinear(s, op.ExpandDims(s, op.Cast(s, decodedImage, tf.Float), op.Const(s.SubScope("make_batch"), int32(0))), - op.Const(s.SubScope("size"), []int32{int32(resolution), int32(resolution)})), + op.Const(s.SubScope("size"), []int32{size, size})), op.Const(s.SubScope("mean"), Mean)), op.Const(s.SubScope("scale"), Scale)) diff --git a/internal/ai/tensorflow/image_test.go b/internal/ai/tensorflow/image_test.go index 7dc857a1b..7bc0511ae 100644 --- a/internal/ai/tensorflow/image_test.go +++ b/internal/ai/tensorflow/image_test.go @@ -32,7 +32,7 @@ func TestConvertStdMean(t *testing.T) { func TestImageFromBytes(t *testing.T) { t.Run("CatJpeg", func(t *testing.T) { - imageBuffer, err := os.ReadFile(examplesPath + "/cat_brown.jpg") + imageBuffer, err := os.ReadFile(filepath.Join(examplesPath, "cat_brown.jpg")) //nolint:gosec // reading bundled test fixture if err != nil { t.Fatal(err) @@ -48,7 +48,7 @@ func TestImageFromBytes(t *testing.T) { assert.Equal(t, int64(224), result.Shape()[2]) }) t.Run("Document", func(t *testing.T) { - imageBuffer, err := os.ReadFile(examplesPath + "/Random.docx") + imageBuffer, err := os.ReadFile(filepath.Join(examplesPath, "Random.docx")) //nolint:gosec // reading bundled test fixture assert.Nil(t, err) result, err := ImageFromBytes(imageBuffer, defaultImageInput, nil) diff --git a/internal/ai/tensorflow/info.go b/internal/ai/tensorflow/info.go index 3483d3ad7..581e45875 100644 --- a/internal/ai/tensorflow/info.go +++ b/internal/ai/tensorflow/info.go @@ -17,7 +17,7 @@ import ( // defined for input images as "what decodeImage returns". const ExpectedChannels = 3 -// Interval of allowed values +// Interval of allowed values. type Interval struct { Start float32 `yaml:"Start,omitempty" json:"start,omitempty"` End float32 `yaml:"End,omitempty" json:"end,omitempty"` @@ -53,9 +53,13 @@ func StandardInterval() *Interval { type ResizeOperation int const ( + // UndefinedResizeOperation indicates that no resize strategy was specified. UndefinedResizeOperation ResizeOperation = iota + // ResizeBreakAspectRatio resizes without preserving aspect ratio. ResizeBreakAspectRatio + // CenterCrop crops the center region after resizing to fill the target size. CenterCrop + // Padding resizes while preserving aspect ratio and pads the rest. Padding ) @@ -74,6 +78,7 @@ func (o ResizeOperation) String() string { } } +// NewResizeOperation parses a string into a ResizeOperation. func NewResizeOperation(s string) (ResizeOperation, error) { switch s { case "Undefined": @@ -85,14 +90,16 @@ func NewResizeOperation(s string) (ResizeOperation, error) { case "Padding": return Padding, nil default: - return UndefinedResizeOperation, fmt.Errorf("Invalid operation %s", s) + return UndefinedResizeOperation, fmt.Errorf("invalid operation %s", s) } } +// MarshalJSON encodes the resize operation as its string name. func (o ResizeOperation) MarshalJSON() ([]byte, error) { return json.Marshal(o.String()) } +// UnmarshalJSON decodes a resize operation from its string representation. func (o *ResizeOperation) UnmarshalJSON(data []byte) error { var s string if err := json.Unmarshal(data, &s); err != nil { @@ -108,10 +115,12 @@ func (o *ResizeOperation) UnmarshalJSON(data []byte) error { return nil } +// MarshalYAML encodes the resize operation for YAML output. func (o ResizeOperation) MarshalYAML() (any, error) { return o.String(), nil } +// UnmarshalYAML decodes the resize operation from YAML input. func (o *ResizeOperation) UnmarshalYAML(unmarshal func(interface{}) error) error { var s string if err := unmarshal(&s); err != nil { @@ -131,15 +140,23 @@ func (o *ResizeOperation) UnmarshalYAML(unmarshal func(interface{}) error) error type ColorChannelOrder int const ( + // UndefinedOrder leaves channel order unspecified, defaulting to RGB. UndefinedOrder ColorChannelOrder = 0 - RGB = 123 - RBG = 132 - GRB = 213 - GBR = 231 - BRG = 312 - BGR = 321 + // RGB represents Red-Green-Blue channel order. + RGB = 123 + // RBG represents Red-Blue-Green channel order. + RBG = 132 + // GRB represents Green-Red-Blue channel order. + GRB = 213 + // GBR represents Green-Blue-Red channel order. + GBR = 231 + // BRG represents Blue-Red-Green channel order. + BRG = 312 + // BGR represents Blue-Green-Red channel order. + BGR = 321 ) +// Indices returns the zero-based indices of the R, G, and B channels. func (o ColorChannelOrder) Indices() (r, g, b int) { i := int(o) @@ -147,7 +164,7 @@ func (o ColorChannelOrder) Indices() (r, g, b int) { i = 123 } - for idx := 0; i > 0 && idx < 3; idx += 1 { + for idx := 0; i > 0 && idx < 3; idx++ { remainder := i % 10 i /= 10 @@ -195,9 +212,10 @@ func (o ColorChannelOrder) String() string { return result } +// NewColorChannelOrder parses a string (e.g., "RGB") into a ColorChannelOrder. func NewColorChannelOrder(val string) (ColorChannelOrder, error) { if len(val) != 3 { - return UndefinedOrder, fmt.Errorf("Invalid length, expected 3") + return UndefinedOrder, fmt.Errorf("invalid length, expected 3") } convert := func(c rune) int { @@ -217,17 +235,19 @@ func NewColorChannelOrder(val string) (ColorChannelOrder, error) { for _, c := range val { index := convert(c) if index == 0 { - return UndefinedOrder, fmt.Errorf("Invalid val %c", c) + return UndefinedOrder, fmt.Errorf("invalid val %c", c) } result = result*10 + index } return ColorChannelOrder(result), nil } +// MarshalJSON encodes the channel order as its string name. func (o ColorChannelOrder) MarshalJSON() ([]byte, error) { return json.Marshal(o.String()) } +// UnmarshalJSON decodes a channel order from its string representation. func (o *ColorChannelOrder) UnmarshalJSON(data []byte) error { var s string if err := json.Unmarshal(data, &s); err != nil { @@ -243,10 +263,12 @@ func (o *ColorChannelOrder) UnmarshalJSON(data []byte) error { return nil } +// MarshalYAML encodes the channel order for YAML output. func (o ColorChannelOrder) MarshalYAML() (any, error) { return o.String(), nil } +// UnmarshalYAML decodes the channel order from YAML input. func (o *ColorChannelOrder) UnmarshalYAML(unmarshal func(interface{}) error) error { var s string if err := unmarshal(&s); err != nil { @@ -261,17 +283,22 @@ func (o *ColorChannelOrder) UnmarshalYAML(unmarshal func(interface{}) error) err return nil } -// The expected shape for the input layer of a mode. Usually this shape is -// (batch, resolution, resolution, channels) but sometimes it is not. +// ShapeComponent describes a single dimension of a model input shape. +// Usually this shape is (batch, resolution, resolution, channels) but sometimes it is not. type ShapeComponent string const ( - ShapeBatch ShapeComponent = "Batch" - ShapeWidth = "Width" - ShapeHeight = "Height" - ShapeColor = "Color" + // ShapeBatch represents the batch dimension. + ShapeBatch ShapeComponent = "Batch" + // ShapeWidth represents the width dimension. + ShapeWidth = "Width" + // ShapeHeight represents the height dimension. + ShapeHeight = "Height" + // ShapeColor represents the color/channel dimension. + ShapeColor = "Color" ) +// DefaultPhotoInputShape returns the standard BHWC input shape. func DefaultPhotoInputShape() []ShapeComponent { return []ShapeComponent{ ShapeBatch, @@ -285,7 +312,7 @@ func DefaultPhotoInputShape() []ShapeComponent { type PhotoInput struct { Name string `yaml:"Name,omitempty" json:"name,omitempty"` Intervals []Interval `yaml:"Intervals,omitempty" json:"intervals,omitempty"` - ResizeOperation ResizeOperation `yaml:"ResizeOperation,omitempty" json:"resizeOperation,omitemty"` + ResizeOperation ResizeOperation `yaml:"ResizeOperation,omitempty" json:"resizeOperation,omitempty"` ColorChannelOrder ColorChannelOrder `yaml:"ColorChannelOrder,omitempty" json:"inputOrder,omitempty"` OutputIndex int `yaml:"Index,omitempty" json:"index,omitempty"` Height int64 `yaml:"Height,omitempty" json:"height,omitempty"` @@ -427,10 +454,11 @@ func (m ModelInfo) IsComplete() bool { return m.Input != nil && m.Output != nil && m.Input.Shape != nil } +// GetModelTagsInfo reads a SavedModel and returns its available meta graph tags. func GetModelTagsInfo(savedModelPath string) ([]ModelInfo, error) { savedModel := filepath.Join(savedModelPath, "saved_model.pb") - data, err := os.ReadFile(savedModel) + data, err := os.ReadFile(savedModel) //nolint:gosec // savedModel path derived from trusted model directory if err != nil { return nil, fmt.Errorf("vision: failed to read %s (%s)", clean.Path(savedModel), clean.Error(err)) diff --git a/internal/ai/tensorflow/info_test.go b/internal/ai/tensorflow/info_test.go index 7ffaa4eff..21fca576a 100644 --- a/internal/ai/tensorflow/info_test.go +++ b/internal/ai/tensorflow/info_test.go @@ -24,11 +24,12 @@ func TestGetModelTagsInfo(t *testing.T) { t.Fatal(err) } - if len(info) != 1 { + switch { + case len(info) != 1: t.Fatalf("Expected 1 info but got %d", len(info)) - } else if len(info[0].Tags) != 1 { + case len(info[0].Tags) != 1: t.Fatalf("Expected 1 tag, but got %d", len(info[0].Tags)) - } else if info[0].Tags[0] != "photoprism" { + case info[0].Tags[0] != "photoprism": t.Fatalf("Expected tag photoprism, but have %s", info[0].Tags[0]) } } diff --git a/internal/ai/tensorflow/labels.go b/internal/ai/tensorflow/labels.go index c5e9f084f..4d5f552eb 100644 --- a/internal/ai/tensorflow/labels.go +++ b/internal/ai/tensorflow/labels.go @@ -12,7 +12,7 @@ import ( func loadLabelsFromPath(path string) (labels []string, err error) { log.Infof("vision: loading TensorFlow model labels from %s", path) - f, err := os.Open(path) + f, err := os.Open(path) //nolint:gosec // path originates from known model directory; reading labels is expected if err != nil { return nil, err } diff --git a/internal/ai/tensorflow/model.go b/internal/ai/tensorflow/model.go index 580b7c619..ebfbe9f32 100644 --- a/internal/ai/tensorflow/model.go +++ b/internal/ai/tensorflow/model.go @@ -23,7 +23,7 @@ func SavedModel(modelPath string, tags []string) (model *tf.SavedModel, err erro } // GuessInputAndOutput tries to inspect a loaded saved model to build the -// ModelInfo struct +// ModelInfo struct. func GuessInputAndOutput(model *tf.SavedModel) (input *PhotoInput, output *ModelOutput, err error) { if model == nil { return nil, nil, fmt.Errorf("tensorflow: GuessInputAndOutput received a nil input") @@ -39,9 +39,10 @@ func GuessInputAndOutput(model *tf.SavedModel) (input *PhotoInput, output *Model shape := modelOps[i].Output(0).Shape() var comps []ShapeComponent - if shape.Size(3) == ExpectedChannels { + switch { + case shape.Size(3) == ExpectedChannels: comps = []ShapeComponent{ShapeBatch, ShapeHeight, ShapeWidth, ShapeColor} - } else if shape.Size(1) == ExpectedChannels { // check the channels are 3 + case shape.Size(1) == ExpectedChannels: // check the channels are 3 comps = []ShapeComponent{ShapeBatch, ShapeColor, ShapeHeight, ShapeWidth, ShapeColor} } @@ -69,6 +70,7 @@ func GuessInputAndOutput(model *tf.SavedModel) (input *PhotoInput, output *Model return nil, nil, fmt.Errorf("could not guess the inputs and outputs") } +// GetInputAndOutputFromSavedModel reads signature definitions to derive input/output info. func GetInputAndOutputFromSavedModel(model *tf.SavedModel) (*PhotoInput, *ModelOutput, error) { if model == nil { return nil, nil, fmt.Errorf("GetInputAndOutputFromSavedModel: nil input") @@ -86,18 +88,20 @@ func GetInputAndOutputFromSavedModel(model *tf.SavedModel) (*PhotoInput, *ModelO for _, inputTensor := range inputs { if inputTensor.Shape.NumDimensions() == 4 { var comps []ShapeComponent - if inputTensor.Shape.Size(3) == ExpectedChannels { + + switch { + case inputTensor.Shape.Size(3) == ExpectedChannels: comps = []ShapeComponent{ShapeBatch, ShapeHeight, ShapeWidth, ShapeColor} - } else if inputTensor.Shape.Size(1) == ExpectedChannels { // check the channels are 3 + case inputTensor.Shape.Size(1) == ExpectedChannels: // check the channels are 3 comps = []ShapeComponent{ShapeBatch, ShapeColor, ShapeHeight, ShapeWidth} - } else { + default: log.Debugf("tensorflow: shape %d", inputTensor.Shape.Size(1)) } if comps == nil { log.Warnf("tensorflow: skipping signature %v because we could not find the color component", k) } else { - var inputIdx = 0 + inputIdx := 0 var err error inputName, inputIndex, found := strings.Cut(inputTensor.Name, ":") diff --git a/internal/ai/tensorflow/model_test.go b/internal/ai/tensorflow/model_test.go index 58c1aaae6..91fa51465 100644 --- a/internal/ai/tensorflow/model_test.go +++ b/internal/ai/tensorflow/model_test.go @@ -20,23 +20,24 @@ func TestTF1ModelLoad(t *testing.T) { t.Fatal(err) } - input, output, err := GetInputAndOutputFromSavedModel(model) + _, _, err = GetInputAndOutputFromSavedModel(model) if err == nil { t.Fatalf("TF1 does not have signatures, but GetInput worked") } - input, output, err = GuessInputAndOutput(model) + input, output, err := GuessInputAndOutput(model) if err != nil { t.Fatal(err) } - if input == nil { + switch { + case input == nil: t.Fatal("Could not get the input") - } else if output == nil { + case output == nil: t.Fatal("Could not get the output") - } else if input.Shape == nil { + case input.Shape == nil: t.Fatal("Could not get the shape") - } else { + default: t.Logf("Shape: %v", input.Shape) } } @@ -55,15 +56,15 @@ func TestTF2ModelLoad(t *testing.T) { t.Fatal(err) } - if input == nil { + switch { + case input == nil: t.Fatal("Could not get the input") - } else if output == nil { + case output == nil: t.Fatal("Could not get the output") - } else if input.Shape == nil { + case input.Shape == nil: t.Fatal("Could not get the shape") - } else if !slices.Equal(input.Shape, DefaultPhotoInputShape()) { - t.Fatalf("Invalid shape calculated. Expected BHWC, got %v", - input.Shape) + case !slices.Equal(input.Shape, DefaultPhotoInputShape()): + t.Fatalf("Invalid shape calculated. Expected BHWC, got %v", input.Shape) } } @@ -81,16 +82,16 @@ func TestTF2ModelBCHWLoad(t *testing.T) { t.Fatal(err) } - if input == nil { + switch { + case input == nil: t.Fatal("Could not get the input") - } else if output == nil { + case output == nil: t.Fatal("Could not get the output") - } else if input.Shape == nil { + case input.Shape == nil: t.Fatal("Could not get the shape") - } else if !slices.Equal(input.Shape, []ShapeComponent{ + case !slices.Equal(input.Shape, []ShapeComponent{ ShapeBatch, ShapeColor, ShapeHeight, ShapeWidth, - }) { - t.Fatalf("Invalid shape calculated. Expected BCHW, got %v", - input.Shape) + }): + t.Fatalf("Invalid shape calculated. Expected BCHW, got %v", input.Shape) } } diff --git a/internal/ai/tensorflow/op.go b/internal/ai/tensorflow/op.go index 9d954ffa5..15bc53d60 100644 --- a/internal/ai/tensorflow/op.go +++ b/internal/ai/tensorflow/op.go @@ -4,6 +4,7 @@ import ( tf "github.com/wamuir/graft/tensorflow" ) +// AddSoftmax appends a Softmax operation to the graph for the configured model output. func AddSoftmax(graph *tf.Graph, info *ModelInfo) (*tf.Operation, error) { randomName := randomString(10) diff --git a/internal/ai/tensorflow/tensor_builder.go b/internal/ai/tensorflow/tensor_builder.go index abe8b141b..2e25a157b 100644 --- a/internal/ai/tensorflow/tensor_builder.go +++ b/internal/ai/tensorflow/tensor_builder.go @@ -7,6 +7,7 @@ import ( tf "github.com/wamuir/graft/tensorflow" ) +// ImageTensorBuilder incrementally constructs an image tensor in BHWC or BCHW order. type ImageTensorBuilder struct { data []float32 shape []ShapeComponent @@ -29,6 +30,7 @@ func shapeLen(c ShapeComponent, res int) int { } } +// NewImageTensorBuilder creates a builder for the given photo input definition. func NewImageTensorBuilder(input *PhotoInput) (*ImageTensorBuilder, error) { if len(input.Shape) != 4 { @@ -62,6 +64,7 @@ func NewImageTensorBuilder(input *PhotoInput) (*ImageTensorBuilder, error) { }, nil } +// Set assigns the normalized RGB values for the pixel at (x,y). func (t *ImageTensorBuilder) Set(x, y int, r, g, b float32) { t.data[t.flatIndex(x, y, t.rIndex)] = r t.data[t.flatIndex(x, y, t.gIndex)] = g @@ -93,6 +96,7 @@ func (t *ImageTensorBuilder) flatIndex(x, y, c int) int { return idx } +// BuildTensor materializes the underlying data into a TensorFlow tensor. func (t *ImageTensorBuilder) BuildTensor() (*tf.Tensor, error) { arr := make([][][][]float32, shapeLen(t.shape[0], t.resolution)) diff --git a/internal/ai/tensorflow/util.go b/internal/ai/tensorflow/util.go index ce51f9dac..46b189bc0 100644 --- a/internal/ai/tensorflow/util.go +++ b/internal/ai/tensorflow/util.go @@ -6,11 +6,12 @@ func randomString(length int) string { const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" result := make([]byte, length) for i := range result { - result[i] = charset[rand.IntN(len(charset))] + result[i] = charset[rand.IntN(len(charset))] //nolint:gosec // pseudo-random is sufficient for non-cryptographic identifiers } return string(result) } +// GetOne returns an arbitrary key-value pair from the map or nils when empty. func GetOne[K comparable, V any](input map[K]V) (*K, *V) { for k, v := range input { return &k, &v @@ -19,6 +20,7 @@ func GetOne[K comparable, V any](input map[K]V) (*K, *V) { return nil, nil } +// Deref returns the value of a pointer or a default when the pointer is nil. func Deref[V any](input *V, defval V) V { if input == nil { return defval From c909c0bd5a180b0047b228399b239e00e31f1f00 Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sat, 22 Nov 2025 11:55:19 +0100 Subject: [PATCH 022/195] CI: Apply Go linter recommendations to "thumb" package #5330 Signed-off-by: Michael Mayer --- internal/thumb/color.go | 1 + internal/thumb/errors.go | 1 + internal/thumb/fileinfo.go | 12 +++++------ internal/thumb/generator.go | 1 + internal/thumb/init.go | 40 ++++++++++++++++++++++--------------- internal/thumb/memsize.go | 2 +- internal/thumb/open_jpeg.go | 5 ++--- internal/thumb/options.go | 2 ++ internal/thumb/resample.go | 11 +++++----- internal/thumb/rotate.go | 17 ++++++++-------- internal/thumb/sizes.go | 9 ++++++--- internal/thumb/thumb.go | 12 +++++------ internal/thumb/video.go | 16 +++++++-------- internal/thumb/vips.go | 9 +++++---- internal/thumb/vips_init.go | 2 +- 15 files changed, 78 insertions(+), 62 deletions(-) diff --git a/internal/thumb/color.go b/internal/thumb/color.go index 3bb5c6149..15886ffd5 100644 --- a/internal/thumb/color.go +++ b/internal/thumb/color.go @@ -2,6 +2,7 @@ package thumb import "github.com/photoprism/photoprism/pkg/clean" +// ColorSpace represents a thumbnail color profile preference. type ColorSpace = string // Supported thumbnail color profile settings. diff --git a/internal/thumb/errors.go b/internal/thumb/errors.go index 0b15ffb79..1e972ae54 100644 --- a/internal/thumb/errors.go +++ b/internal/thumb/errors.go @@ -5,5 +5,6 @@ import ( ) var ( + // ErrNotCached indicates a requested thumbnail is not present in cache. ErrNotCached = errors.New("not cached") ) diff --git a/internal/thumb/fileinfo.go b/internal/thumb/fileinfo.go index 6e9e65e58..bd0b0540b 100644 --- a/internal/thumb/fileinfo.go +++ b/internal/thumb/fileinfo.go @@ -3,14 +3,14 @@ package thumb import ( "fmt" "image" - _ "image/gif" - _ "image/jpeg" - _ "image/png" + _ "image/gif" // register GIF decoder for config reads + _ "image/jpeg" // register JPEG decoder for config reads + _ "image/png" // register PNG decoder for config reads "os" "runtime/debug" - _ "golang.org/x/image/bmp" - _ "golang.org/x/image/webp" + _ "golang.org/x/image/bmp" // register BMP decoder for config reads + _ "golang.org/x/image/webp" // register WEBP decoder for config reads "github.com/photoprism/photoprism/pkg/clean" "github.com/photoprism/photoprism/pkg/fs" @@ -29,7 +29,7 @@ func FileInfo(fileName string) (info image.Config, err error) { return info, err } - file, err := os.Open(fileName) + file, err := os.Open(fileName) //nolint:gosec // fileName is resolved path provided by caller; reading images is expected if err != nil || file == nil { return info, err diff --git a/internal/thumb/generator.go b/internal/thumb/generator.go index 88b13a285..cc0bf94c2 100644 --- a/internal/thumb/generator.go +++ b/internal/thumb/generator.go @@ -1,5 +1,6 @@ package thumb +// Lib identifies the image processing backend. type Lib = string // Supported image processing libraries. diff --git a/internal/thumb/init.go b/internal/thumb/init.go index 5af0146fd..778172ace 100644 --- a/internal/thumb/init.go +++ b/internal/thumb/init.go @@ -3,19 +3,29 @@ package thumb import "github.com/dustin/go-humanize/english" const ( - MiB = 1024 * 1024 - GiB = 1024 * MiB - DefaultCacheMem = 128 * MiB - DefaultCacheSize = 128 + // MiB represents one mebibyte. + MiB = 1024 * 1024 + // GiB represents one gibibyte. + GiB = 1024 * MiB + // DefaultCacheMem specifies the default libvips cache memory limit. + DefaultCacheMem = 128 * MiB + // DefaultCacheSize is the default number of cached operations. + DefaultCacheSize = 128 + // DefaultCacheFiles is the default number of cached files. DefaultCacheFiles = 16 - DefaultWorkers = 1 + // DefaultWorkers is the default worker count when not specified. + DefaultWorkers = 1 ) var ( - MaxCacheMem = DefaultCacheMem - MaxCacheSize = DefaultCacheSize + // MaxCacheMem is the maximum memory libvips may use for caching. + MaxCacheMem = DefaultCacheMem + // MaxCacheSize limits the number of cached operations. + MaxCacheSize = DefaultCacheSize + // MaxCacheFiles limits the number of cached files. MaxCacheFiles = DefaultCacheFiles - NumWorkers = DefaultWorkers + // NumWorkers defines the number of libvips worker threads. + NumWorkers = DefaultWorkers ) // Init initializes the package config based on the available memory, @@ -38,14 +48,12 @@ func Init(availableMemory uint64, maxWorkers int, imgLib string) { } // Set the number of worker threads that libvips can use. - if maxWorkers > 0 { - // Using the specified number of workers. - NumWorkers = maxWorkers - } else if maxWorkers < 0 { - // Using built-in default. - NumWorkers = 0 - } else { - // Default to one worker. + switch { + case maxWorkers > 0: + NumWorkers = maxWorkers // explicitly configured + case maxWorkers < 0: + NumWorkers = 0 // use libvips default + default: NumWorkers = DefaultWorkers } diff --git a/internal/thumb/memsize.go b/internal/thumb/memsize.go index f29e32a02..90d98fb0d 100644 --- a/internal/thumb/memsize.go +++ b/internal/thumb/memsize.go @@ -57,5 +57,5 @@ func MemSize(img image.Image) Bytes { bytesPerPixel = 8 } - return Bytes(pixels * bytesPerPixel) + return Bytes(uint64(pixels) * uint64(bytesPerPixel)) //nolint:gosec // pixels and bytesPerPixel are non-negative and bounded by image dimensions } diff --git a/internal/thumb/open_jpeg.go b/internal/thumb/open_jpeg.go index e1e968ac3..294f298ef 100644 --- a/internal/thumb/open_jpeg.go +++ b/internal/thumb/open_jpeg.go @@ -49,7 +49,7 @@ func OpenJpeg(fileName string, orientation int) (image.Image, error) { logName := clean.Log(filepath.Base(fileName)) // Open file. - fileReader, err := os.Open(fileName) + fileReader, err := os.Open(fileName) //nolint:gosec // fileName is provided by caller and validated earlier if err != nil { return nil, err } @@ -76,8 +76,7 @@ func OpenJpeg(fileName string, orientation int) (image.Image, error) { log.Tracef("thumb: %s has no color profile", logName) } else if profile, err := iccProfile.Description(); err == nil && profile != "" { log.Tracef("thumb: %s has color profile %s", logName, clean.Log(profile)) - switch { - case colors.ProfileDisplayP3.Equal(profile): + if colors.ProfileDisplayP3.Equal(profile) { img = colors.ToSRGB(img, colors.ProfileDisplayP3) } } diff --git a/internal/thumb/options.go b/internal/thumb/options.go index 8609f5a35..489db0241 100644 --- a/internal/thumb/options.go +++ b/internal/thumb/options.go @@ -4,6 +4,7 @@ import ( "github.com/photoprism/photoprism/pkg/fs" ) +// ResampleOption enumerates thumbnail resampling strategies. const ( ResampleFillCenter ResampleOption = iota ResampleFillTopLeft @@ -15,6 +16,7 @@ const ( ResamplePng ) +// ResampleMethods maps resample options to their string identifiers. var ResampleMethods = map[ResampleOption]string{ ResampleFillCenter: "center", ResampleFillTopLeft: "left", diff --git a/internal/thumb/resample.go b/internal/thumb/resample.go index f9d3ea1c0..62594d739 100644 --- a/internal/thumb/resample.go +++ b/internal/thumb/resample.go @@ -12,15 +12,16 @@ func Resample(img image.Image, width, height int, opts ...ResampleOption) image. method, filter, _ := ResampleOptions(opts...) - if method == ResampleFit { + switch method { + case ResampleFit: resImg = imaging.Fit(img, width, height, filter.Imaging()) - } else if method == ResampleFillCenter { + case ResampleFillCenter: resImg = imaging.Fill(img, width, height, imaging.Center, filter.Imaging()) - } else if method == ResampleFillTopLeft { + case ResampleFillTopLeft: resImg = imaging.Fill(img, width, height, imaging.TopLeft, filter.Imaging()) - } else if method == ResampleFillBottomRight { + case ResampleFillBottomRight: resImg = imaging.Fill(img, width, height, imaging.BottomRight, filter.Imaging()) - } else if method == ResampleResize { + case ResampleResize: resImg = imaging.Resize(img, width, height, filter.Imaging()) } diff --git a/internal/thumb/rotate.go b/internal/thumb/rotate.go index e624d98df..3bf477f24 100644 --- a/internal/thumb/rotate.go +++ b/internal/thumb/rotate.go @@ -6,16 +6,17 @@ import ( "github.com/disintegration/imaging" ) +// EXIF orientation values. const ( OrientationUnspecified int = 0 - OrientationNormal = 1 - OrientationFlipH = 2 - OrientationRotate180 = 3 - OrientationFlipV = 4 - OrientationTranspose = 5 - OrientationRotate270 = 6 - OrientationTransverse = 7 - OrientationRotate90 = 8 + OrientationNormal int = 1 + OrientationFlipH int = 2 + OrientationRotate180 int = 3 + OrientationFlipV int = 4 + OrientationTranspose int = 5 + OrientationRotate270 int = 6 + OrientationTransverse int = 7 + OrientationRotate90 int = 8 ) // Rotate rotates an image based on the Exif orientation. diff --git a/internal/thumb/sizes.go b/internal/thumb/sizes.go index 066630a1c..76b226984 100644 --- a/internal/thumb/sizes.go +++ b/internal/thumb/sizes.go @@ -33,11 +33,12 @@ func (m SizeMap) All() SizeList { } slices.SortStableFunc(result, func(a, b Size) int { - if a.Width < b.Width { + switch { + case a.Width < b.Width: return -1 - } else if a.Width > b.Width { + case a.Width > b.Width: return 1 - } else { + default: return 0 } }) @@ -45,6 +46,7 @@ func (m SizeMap) All() SizeList { return result } +// Size presets used throughout the application. var ( SizeColors = Size{Colors, Fit720, "Color Detection", 3, 3, false, false, false, true, Options{ResampleResize, ResampleNearestNeighbor, ResamplePng}} SizeTile50 = Size{Tile50, Fit720, "List View", 50, 50, false, false, false, true, Options{ResampleFillCenter, ResampleDefault}} @@ -103,6 +105,7 @@ var Sizes = SizeMap{ // All contains all thumbnail sizes sorted by width. var All = Sizes.All() +// ParseSize returns a Size by name or the zero value if unknown. func ParseSize(s string) Size { return Sizes[Name(s)] } diff --git a/internal/thumb/thumb.go b/internal/thumb/thumb.go index 79fa9b19d..e8b1f622b 100644 --- a/internal/thumb/thumb.go +++ b/internal/thumb/thumb.go @@ -26,14 +26,14 @@ package thumb import ( "fmt" - _ "image/gif" - _ "image/jpeg" - _ "image/png" + _ "image/gif" // register GIF decoder for thumbnail generation + _ "image/jpeg" // register JPEG decoder for thumbnail generation + _ "image/png" // register PNG decoder for thumbnail generation "math" - _ "golang.org/x/image/bmp" - _ "golang.org/x/image/tiff" - _ "golang.org/x/image/webp" + _ "golang.org/x/image/bmp" // register BMP decoder for thumbnail generation + _ "golang.org/x/image/tiff" // register TIFF decoder for thumbnail generation + _ "golang.org/x/image/webp" // register WEBP decoder for thumbnail generation "github.com/photoprism/photoprism/internal/event" ) diff --git a/internal/thumb/video.go b/internal/thumb/video.go index 0f1c349bb..5ae6bad36 100644 --- a/internal/thumb/video.go +++ b/internal/thumb/video.go @@ -14,15 +14,13 @@ var VideoSizes = SizeList{ // VideoSize returns the largest video size type for the given width limit. func VideoSize(limit int) Size { - if limit < 0 { - // Return maximum size. - return Sizes[Fit7680] - } else if limit == 0 { - // Return default size. - return Sizes[Fit4096] - } else if limit <= 720 { - // Return minimum size. - return Sizes[Fit720] + switch { + case limit < 0: + return Sizes[Fit7680] // maximum + case limit == 0: + return Sizes[Fit4096] // default + case limit <= 720: + return Sizes[Fit720] // minimum } // Find match. diff --git a/internal/thumb/vips.go b/internal/thumb/vips.go index aa1dd0000..115b1a1c2 100644 --- a/internal/thumb/vips.go +++ b/internal/thumb/vips.go @@ -62,16 +62,17 @@ func Vips(imageName string, imageBuffer []byte, hash, thumbPath string, width, h // Choose thumbnail crop. var crop vips.Interesting - if method == ResampleFillTopLeft { + switch method { + case ResampleFillTopLeft: crop = vips.InterestingLow size = vips.SizeBoth - } else if method == ResampleFillBottomRight { + case ResampleFillBottomRight: crop = vips.InterestingHigh size = vips.SizeBoth - } else if method == ResampleFit { + case ResampleFit: crop = vips.InterestingNone size = vips.SizeDown - } else if method == ResampleFillCenter || method == ResampleResize { + case ResampleFillCenter, ResampleResize: crop = vips.InterestingCentre size = vips.SizeBoth } diff --git a/internal/thumb/vips_init.go b/internal/thumb/vips_init.go index 066bddd18..0cad26708 100644 --- a/internal/thumb/vips_init.go +++ b/internal/thumb/vips_init.go @@ -29,7 +29,7 @@ func VipsShutdown() { // vipsInit calls vips.Startup() to initialize libvips. func vipsInit() { - if vipsStarted == true { + if vipsStarted { log.Warnf("vips: already initialized - you may have found a bug") return } From 7391066fb01e4e8c71d2f4bfd721220cb57e1fd6 Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sat, 22 Nov 2025 12:09:21 +0100 Subject: [PATCH 023/195] CI: Apply Go linter recommendations to "internal/form" package #5330 Signed-off-by: Michael Mayer --- internal/form/client.go | 14 +++++++------ internal/form/client_test.go | 4 ++-- internal/form/face.go | 1 + internal/form/feedback.go | 2 ++ internal/form/file.go | 1 + internal/form/folder.go | 1 + internal/form/import_options.go | 1 + internal/form/index_options.go | 1 + internal/form/oauth_create_token.go | 32 ++++++++++++++++------------- internal/form/oauth_revoke_token.go | 16 +++++++++------ internal/form/search.go | 2 ++ internal/form/search_albums.go | 4 ++++ internal/form/search_faces.go | 4 ++++ internal/form/search_folders.go | 3 +++ internal/form/search_labels.go | 4 ++++ internal/form/search_photos.go | 4 ++++ internal/form/search_photos_geo.go | 1 + internal/form/search_subjects.go | 4 ++++ internal/form/search_users.go | 3 +++ internal/form/serialize.go | 18 ++++++++++------ internal/form/service_search.go | 4 ++++ internal/form/service_upload.go | 1 + internal/form/subject.go | 1 + internal/form/upload_options.go | 1 + 24 files changed, 93 insertions(+), 34 deletions(-) diff --git a/internal/form/client.go b/internal/form/client.go index 2299f7dea..498acfe3a 100644 --- a/internal/form/client.go +++ b/internal/form/client.go @@ -192,11 +192,12 @@ func (f Client) Scope() string { // Expires returns the access token expiry time in seconds or 0 if not specified. func (f Client) Expires() int64 { - if f.AuthExpires > unix.Month { + switch { + case f.AuthExpires > unix.Month: return unix.Month - } else if f.AuthExpires > 0 { + case f.AuthExpires > 0: return f.AuthExpires - } else if f.AuthExpires < 0 { + case f.AuthExpires < 0: return unix.Hour } @@ -205,11 +206,12 @@ func (f Client) Expires() int64 { // Tokens returns the access token limit or 0 if not specified. func (f Client) Tokens() int64 { - if f.AuthTokens > 2147483647 { + switch { + case f.AuthTokens > 2147483647: return 2147483647 - } else if f.AuthTokens > 0 { + case f.AuthTokens > 0: return f.AuthTokens - } else if f.AuthTokens < 0 { + case f.AuthTokens < 0: return -1 } diff --git a/internal/form/client_test.go b/internal/form/client_test.go index 6f7b4ab76..edb3e74c5 100644 --- a/internal/form/client_test.go +++ b/internal/form/client_test.go @@ -40,7 +40,7 @@ func TestAddClientFromCli(t *testing.T) { assert.NoError(t, ctx.Set("provider", "client_credentials")) assert.NoError(t, ctx.Set("method", "oauth2")) - //t.Logf("ARGS: %#v", ctx.Args()) + // t.Logf("ARGS: %#v", ctx.Args()) // Check flag values. assert.True(t, ctx.IsSet("name")) @@ -69,7 +69,7 @@ func TestAddClientFromCli(t *testing.T) { assert.NoError(t, ctx.Set("provider", "")) assert.NoError(t, ctx.Set("method", "")) - //t.Logf("ARGS: %#v", ctx.Args()) + // t.Logf("ARGS: %#v", ctx.Args()) // Check flag values. assert.True(t, ctx.IsSet("id")) diff --git a/internal/form/face.go b/internal/form/face.go index 443490a51..267a4d81e 100644 --- a/internal/form/face.go +++ b/internal/form/face.go @@ -8,6 +8,7 @@ type Face struct { SubjUID string `json:"SubjUID"` } +// NewFace copies values from an arbitrary model into a Face form. func NewFace(m interface{}) (f Face, err error) { err = deepcopier.Copy(m).To(&f) diff --git a/internal/form/feedback.go b/internal/form/feedback.go index 15d2cc432..615031348 100644 --- a/internal/form/feedback.go +++ b/internal/form/feedback.go @@ -12,10 +12,12 @@ type Feedback struct { UserLocales string `json:"UserLocales"` } +// Empty reports whether the feedback form lacks required content. func (f Feedback) Empty() bool { return len(f.Category) < 1 || len(f.Message) < 3 || len(f.UserEmail) < 5 } +// NewFeedback copies values from an arbitrary model into a Feedback form. func NewFeedback(m interface{}) (f Feedback, err error) { err = deepcopier.Copy(m).To(&f) diff --git a/internal/form/file.go b/internal/form/file.go index 6cf5880c0..c73274a57 100644 --- a/internal/form/file.go +++ b/internal/form/file.go @@ -16,6 +16,7 @@ func (f *File) Orientation() int { return clean.Orientation(f.FileOrientation) } +// NewFile copies values from an arbitrary model into a File form. func NewFile(m interface{}) (f File, err error) { err = deepcopier.Copy(m).To(&f) diff --git a/internal/form/folder.go b/internal/form/folder.go index 72dda0b76..d0da3edb8 100644 --- a/internal/form/folder.go +++ b/internal/form/folder.go @@ -20,6 +20,7 @@ type Folder struct { FolderWatch bool `json:"Watch"` } +// NewFolder copies values from an arbitrary model into a Folder form. func NewFolder(m interface{}) (f Folder, err error) { err = deepcopier.Copy(m).To(&f) diff --git a/internal/form/import_options.go b/internal/form/import_options.go index d1a5190af..c4a0818fc 100644 --- a/internal/form/import_options.go +++ b/internal/form/import_options.go @@ -1,5 +1,6 @@ package form +// ImportOptions holds import path and album assignment flags. type ImportOptions struct { Albums []string `json:"albums"` Path string `json:"path"` diff --git a/internal/form/index_options.go b/internal/form/index_options.go index d8177015a..c59c75503 100644 --- a/internal/form/index_options.go +++ b/internal/form/index_options.go @@ -1,5 +1,6 @@ package form +// IndexOptions configure index paths and maintenance flags. type IndexOptions struct { Path string `json:"path"` Rescan bool `json:"rescan"` diff --git a/internal/form/oauth_create_token.go b/internal/form/oauth_create_token.go index 8285b957c..1abd0f2b0 100644 --- a/internal/form/oauth_create_token.go +++ b/internal/form/oauth_create_token.go @@ -29,42 +29,46 @@ func (f OAuthCreateToken) Validate() error { switch f.GrantType { case authn.GrantClientCredentials, authn.GrantUndefined: // Validate client id. - if f.ClientID == "" { + switch { + case f.ClientID == "": return authn.ErrClientIDRequired - } else if rnd.InvalidUID(f.ClientID, 'c') { + case rnd.InvalidUID(f.ClientID, 'c'): return authn.ErrInvalidCredentials } // Validate client secret. - if f.ClientSecret == "" { + switch { + case f.ClientSecret == "": return authn.ErrClientSecretRequired - } else if !rnd.IsAlnum(f.ClientSecret) { + case !rnd.IsAlnum(f.ClientSecret): return authn.ErrInvalidCredentials } case authn.GrantSession: // Validate request credentials. - if f.Username == "" { + switch { + case f.Username == "": return authn.ErrUsernameRequired - } else if len(f.Username) > txt.ClipUsername { + case len(f.Username) > txt.ClipUsername: return authn.ErrInvalidCredentials - } else if f.ClientName == "" { + case f.ClientName == "": return authn.ErrNameRequired - } else if f.Scope == "" { + case f.Scope == "": return authn.ErrScopeRequired } case authn.GrantPassword: // Validate request credentials. - if f.Username == "" { + switch { + case f.Username == "": return authn.ErrUsernameRequired - } else if len(f.Username) > txt.ClipUsername { + case len(f.Username) > txt.ClipUsername: return authn.ErrInvalidCredentials - } else if f.Password == "" { + case f.Password == "": return authn.ErrPasswordRequired - } else if len(f.Password) > txt.ClipPassword { + case len(f.Password) > txt.ClipPassword: return authn.ErrInvalidCredentials - } else if f.ClientName == "" { + case f.ClientName == "": return authn.ErrNameRequired - } else if f.Scope == "" { + case f.Scope == "": return authn.ErrScopeRequired } default: diff --git a/internal/form/oauth_revoke_token.go b/internal/form/oauth_revoke_token.go index 0c5467ab6..1d3ebdce5 100644 --- a/internal/form/oauth_revoke_token.go +++ b/internal/form/oauth_revoke_token.go @@ -6,8 +6,11 @@ import ( ) const ( - RefID = "ref_id" - SessionID = "session_id" + // RefID indicates a reference token identifier. + RefID = "ref_id" + // SessionID indicates a session token identifier. + SessionID = "session_id" + // AccessToken indicates a bearer access token identifier. AccessToken = "access_token" ) @@ -43,13 +46,14 @@ func (f *OAuthRevokeToken) Validate() error { switch f.TokenTypeHint { case "": - if !isRefID && !isSessionID && !isAuthAny { + switch { + case !isRefID && !isSessionID && !isAuthAny: return authn.ErrInvalidToken - } else if isRefID { + case isRefID: f.TokenTypeHint = RefID - } else if isSessionID { + case isSessionID: f.TokenTypeHint = SessionID - } else { + default: f.TokenTypeHint = AccessToken } case RefID: diff --git a/internal/form/search.go b/internal/form/search.go index 5628aa29c..9c6ac6751 100644 --- a/internal/form/search.go +++ b/internal/form/search.go @@ -1,10 +1,12 @@ package form +// SearchForm defines the minimal interface for query string parsing. type SearchForm interface { GetQuery() string SetQuery(q string) } +// ParseQueryString populates the search form fields from its query string. func ParseQueryString(f SearchForm) (result error) { q := f.GetQuery() diff --git a/internal/form/search_albums.go b/internal/form/search_albums.go index 8cc558b8b..b2f0e8e1b 100644 --- a/internal/form/search_albums.go +++ b/internal/form/search_albums.go @@ -22,18 +22,22 @@ type SearchAlbums struct { Reverse bool `form:"reverse" serialize:"-"` } +// GetQuery returns the current search query string. func (f *SearchAlbums) GetQuery() string { return f.Query } +// SetQuery stores the raw query string. func (f *SearchAlbums) SetQuery(q string) { f.Query = q } +// ParseQueryString deserializes the query string into form fields. func (f *SearchAlbums) ParseQueryString() error { return ParseQueryString(f) } +// NewAlbumSearch creates a SearchAlbums form with the provided query. func NewAlbumSearch(query string) SearchAlbums { return SearchAlbums{Query: query} } diff --git a/internal/form/search_faces.go b/internal/form/search_faces.go index 620de1e65..de0203740 100644 --- a/internal/form/search_faces.go +++ b/internal/form/search_faces.go @@ -14,18 +14,22 @@ type SearchFaces struct { Reverse bool `form:"reverse" serialize:"-"` } +// GetQuery returns the current search query string. func (f *SearchFaces) GetQuery() string { return f.Query } +// SetQuery stores the raw query string. func (f *SearchFaces) SetQuery(q string) { f.Query = q } +// ParseQueryString deserializes the query string into form fields. func (f *SearchFaces) ParseQueryString() error { return ParseQueryString(f) } +// NewFaceSearch creates a SearchFaces form with the provided query. func NewFaceSearch(query string) SearchFaces { return SearchFaces{Query: query} } diff --git a/internal/form/search_folders.go b/internal/form/search_folders.go index b8b01987c..dbdf7a0e8 100644 --- a/internal/form/search_folders.go +++ b/internal/form/search_folders.go @@ -11,14 +11,17 @@ type SearchFolders struct { Offset int `form:"offset" serialize:"-"` } +// GetQuery returns the current search query string. func (f *SearchFolders) GetQuery() string { return f.Query } +// SetQuery stores the raw query string. func (f *SearchFolders) SetQuery(q string) { f.Query = q } +// ParseQueryString deserializes the query string into form fields. func (f *SearchFolders) ParseQueryString() error { return ParseQueryString(f) } diff --git a/internal/form/search_labels.go b/internal/form/search_labels.go index 02c0f8ba9..bf3f0a88a 100644 --- a/internal/form/search_labels.go +++ b/internal/form/search_labels.go @@ -16,18 +16,22 @@ type SearchLabels struct { Reverse bool `form:"reverse" serialize:"-"` } +// GetQuery returns the current search query string. func (f *SearchLabels) GetQuery() string { return f.Query } +// SetQuery stores the raw query string. func (f *SearchLabels) SetQuery(q string) { f.Query = q } +// ParseQueryString deserializes the query string into form fields. func (f *SearchLabels) ParseQueryString() error { return ParseQueryString(f) } +// NewLabelSearch creates a SearchLabels form with the provided query. func NewLabelSearch(query string) SearchLabels { return SearchLabels{Query: query} } diff --git a/internal/form/search_photos.go b/internal/form/search_photos.go index 90921b689..4eb1a69b0 100644 --- a/internal/form/search_photos.go +++ b/internal/form/search_photos.go @@ -103,14 +103,17 @@ type SearchPhotos struct { Details bool `form:"-" serialize:"-"` // Include additional information from details table } +// GetQuery returns the current search query string. func (f *SearchPhotos) GetQuery() string { return f.Query } +// SetQuery stores the raw query string. func (f *SearchPhotos) SetQuery(q string) { f.Query = q } +// ParseQueryString deserializes the query string into form fields and applies aliases. func (f *SearchPhotos) ParseQueryString() error { if err := ParseQueryString(f); err != nil { return err @@ -180,6 +183,7 @@ func (f *SearchPhotos) FindUidOnly() bool { return f.UID != "" && f.Query == "" && f.Scope == "" && f.Filter == "" && f.Album == "" && f.Albums == "" } +// NewSearchPhotos creates a SearchPhotos form with the provided query. func NewSearchPhotos(query string) SearchPhotos { return SearchPhotos{Query: query} } diff --git a/internal/form/search_photos_geo.go b/internal/form/search_photos_geo.go index 8ac8d7c44..d1e1d5a7a 100644 --- a/internal/form/search_photos_geo.go +++ b/internal/form/search_photos_geo.go @@ -166,6 +166,7 @@ func (f *SearchPhotosGeo) FindUidOnly() bool { return f.UID != "" && f.Query == "" && f.Scope == "" && f.Filter == "" && f.Album == "" && f.Albums == "" } +// NewSearchPhotosGeo creates a SearchPhotosGeo form with the provided query. func NewSearchPhotosGeo(query string) SearchPhotosGeo { return SearchPhotosGeo{Query: query} } diff --git a/internal/form/search_subjects.go b/internal/form/search_subjects.go index d25641503..85b09df3c 100644 --- a/internal/form/search_subjects.go +++ b/internal/form/search_subjects.go @@ -19,18 +19,22 @@ type SearchSubjects struct { Reverse bool `form:"reverse" serialize:"-"` } +// GetQuery returns the current search query string. func (f *SearchSubjects) GetQuery() string { return f.Query } +// SetQuery stores the raw query string. func (f *SearchSubjects) SetQuery(q string) { f.Query = q } +// ParseQueryString deserializes the query string into form fields. func (f *SearchSubjects) ParseQueryString() error { return ParseQueryString(f) } +// NewSubjectSearch creates a SearchSubjects form with the provided query. func NewSubjectSearch(query string) SearchSubjects { return SearchSubjects{Query: query} } diff --git a/internal/form/search_users.go b/internal/form/search_users.go index 83cad6168..127f73fd4 100644 --- a/internal/form/search_users.go +++ b/internal/form/search_users.go @@ -14,14 +14,17 @@ type SearchUsers struct { Reverse bool `form:"reverse" serialize:"-"` } +// GetQuery returns the current search query string. func (f *SearchUsers) GetQuery() string { return f.Query } +// SetQuery stores the raw query string. func (f *SearchUsers) SetQuery(q string) { f.Query = q } +// ParseQueryString deserializes the query string into form fields. func (f *SearchUsers) ParseQueryString() error { return ParseQueryString(f) } diff --git a/internal/form/serialize.go b/internal/form/serialize.go index 4d61d4078..dfc1adcb4 100644 --- a/internal/form/serialize.go +++ b/internal/form/serialize.go @@ -76,6 +76,7 @@ func Serialize(f interface{}, all bool) string { return strings.Join(q, " ") } +// Unserialize parses a query string into the provided SearchForm implementation. func Unserialize(f SearchForm, q string) (result error) { var key, value []rune var escaped, isKeyValue bool @@ -110,7 +111,8 @@ func Unserialize(f SearchForm, q string) (result error) { var queryStrings []string for _, char := range q { - if unicode.IsSpace(char) && !escaped { + switch { + case unicode.IsSpace(char) && !escaped: if isKeyValue { formName := strings.ToLower(string(key)) fieldName := fieldNames[formName] @@ -145,7 +147,11 @@ func Unserialize(f SearchForm, q string) (result error) { if intValue, err := strconv.Atoi(stringValue); err != nil { result = err } else { - field.SetUint(uint64(intValue)) + if intValue < 0 { + result = fmt.Errorf("unsupported negative value for %s", formName) + } else { + field.SetUint(uint64(intValue)) + } } case string: field.SetString(clean.SearchString(stringValue)) @@ -165,13 +171,13 @@ func Unserialize(f SearchForm, q string) (result error) { isKeyValue = false key = key[:0] value = value[:0] - } else if char == ':' && !escaped { + case char == ':' && !escaped: isKeyValue = true - } else if char == '"' { + case char == '"': escaped = !escaped - } else if isKeyValue { + case isKeyValue: value = append(value, char) - } else { + default: key = append(key, unicode.ToLower(char)) } } diff --git a/internal/form/service_search.go b/internal/form/service_search.go index 878b1436a..d9e28d972 100644 --- a/internal/form/service_search.go +++ b/internal/form/service_search.go @@ -11,18 +11,22 @@ type SearchServices struct { Order string `form:"order" serialize:"-"` } +// GetQuery returns the current search query string. func (f *SearchServices) GetQuery() string { return f.Query } +// SetQuery stores the raw query string. func (f *SearchServices) SetQuery(q string) { f.Query = q } +// ParseQueryString deserializes the query string into form fields. func (f *SearchServices) ParseQueryString() error { return ParseQueryString(f) } +// NewSearchServices creates a SearchServices form with the provided query. func NewSearchServices(query string) SearchServices { return SearchServices{Query: query} } diff --git a/internal/form/service_upload.go b/internal/form/service_upload.go index 6eec19616..2a166fdd4 100644 --- a/internal/form/service_upload.go +++ b/internal/form/service_upload.go @@ -1,5 +1,6 @@ package form +// SyncUpload defines payload for syncing uploads to a remote service. type SyncUpload struct { Selection Selection `json:"selection"` Folder string `json:"folder"` diff --git a/internal/form/subject.go b/internal/form/subject.go index 9bd06e6e3..28b65afc4 100644 --- a/internal/form/subject.go +++ b/internal/form/subject.go @@ -17,6 +17,7 @@ type Subject struct { ThumbSrc string `json:"ThumbSrc"` } +// NewSubject copies values from an arbitrary model into a Subject form. func NewSubject(m interface{}) (*Subject, error) { frm := &Subject{} err := deepcopier.Copy(m).To(frm) diff --git a/internal/form/upload_options.go b/internal/form/upload_options.go index 6fc7c011f..518db643a 100644 --- a/internal/form/upload_options.go +++ b/internal/form/upload_options.go @@ -1,5 +1,6 @@ package form +// UploadOptions holds album assignment options for uploads. type UploadOptions struct { Albums []string `json:"albums"` } From 10d6ca36b7aee5ee7c4e81715460eb233c0ad7e8 Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sat, 22 Nov 2025 12:38:24 +0100 Subject: [PATCH 024/195] CI: Apply Go linter recommendations to "internal/meta" package #5330 Signed-off-by: Michael Mayer --- internal/meta/exif.go | 4 ++-- internal/meta/exif_parser.go | 7 +++--- internal/meta/exif_test.go | 4 ++-- internal/meta/gps.go | 6 ++--- internal/meta/json.go | 9 ++++---- internal/meta/json_exiftool.go | 4 ++-- internal/meta/json_motion_test.go | 24 +++++++++---------- internal/meta/json_test.go | 38 +++++++++++++++---------------- internal/meta/sanitize.go | 4 ++-- internal/meta/xmp_document.go | 7 ++---- 10 files changed, 53 insertions(+), 54 deletions(-) diff --git a/internal/meta/exif.go b/internal/meta/exif.go index 9af80124a..5365f21b1 100644 --- a/internal/meta/exif.go +++ b/internal/meta/exif.go @@ -306,8 +306,8 @@ func (data *Data) Exif(fileName string, fileFormat fs.Type, bruteForce bool) (er // Add nanoseconds to the calculated UTC and local time. if data.TakenAt.Nanosecond() == 0 { if ns := time.Duration(data.TakenNs); ns > 0 && ns <= time.Second { - data.TakenAt.Truncate(time.Second).UTC().Add(ns) - data.TakenAtLocal.Truncate(time.Second).Add(ns) + data.TakenAt = data.TakenAt.Truncate(time.Second).UTC().Add(ns) + data.TakenAtLocal = data.TakenAtLocal.Truncate(time.Second).Add(ns) } } diff --git a/internal/meta/exif_parser.go b/internal/meta/exif_parser.go index f803acade..e962e5f9b 100644 --- a/internal/meta/exif_parser.go +++ b/internal/meta/exif_parser.go @@ -44,11 +44,12 @@ func RawExif(fileName string, fileFormat fs.Type, bruteForce bool) (rawExif []by _, rawExif, err = sl.Exif() if err != nil { - if !bruteForce || strings.HasPrefix(err.Error(), "no exif header") { + switch { + case !bruteForce || strings.HasPrefix(err.Error(), "no exif header"): return rawExif, fmt.Errorf("found no exif header") - } else if strings.HasPrefix(err.Error(), "no exif data") { + case strings.HasPrefix(err.Error(), "no exif data"): log.Debugf("metadata: failed parsing %s, starting brute-force search (parse jpeg)", logName) - } else { + default: log.Infof("metadata: %s in %s, starting brute-force search (parse jpeg)", err, logName) } } else { diff --git a/internal/meta/exif_test.go b/internal/meta/exif_test.go index f5414a705..24ba03af7 100644 --- a/internal/meta/exif_test.go +++ b/internal/meta/exif_test.go @@ -634,8 +634,8 @@ func TestExif(t *testing.T) { t.Fatal(err) } - assert.Equal(t, "2022-04-24 10:35:53 +0000 UTC", data.TakenAtLocal.String()) - assert.Equal(t, "2022-04-24 02:35:53 +0000 UTC", data.TakenAt.String()) + assert.Equal(t, "2022-04-24 10:35:53.358 +0000 UTC", data.TakenAtLocal.String()) + assert.Equal(t, "2022-04-24 02:35:53.358 +0000 UTC", data.TakenAt.String()) assert.Equal(t, "Asia/Shanghai", data.TimeZone) // Local Time assert.Equal(t, 1, data.Orientation) assert.InEpsilon(t, 33.640007, data.Lat, 0.00001) diff --git a/internal/meta/gps.go b/internal/meta/gps.go index 93ba55709..401584da5 100644 --- a/internal/meta/gps.go +++ b/internal/meta/gps.go @@ -18,9 +18,9 @@ const ( // Regular expressions used to extract GPS coordinate components from EXIF strings. var ( - GpsCoordsRegexp = regexp.MustCompile("[0-9\\.]+") - GpsRefRegexp = regexp.MustCompile("[NSEW]+") - GpsFloatRegexp = regexp.MustCompile("[+\\-]?(?:(?:0|[1-9]\\d*)(?:\\.\\d*)?|\\.\\d+)") + GpsCoordsRegexp = regexp.MustCompile(`[0-9\.]+`) + GpsRefRegexp = regexp.MustCompile(`[NSEW]+`) + GpsFloatRegexp = regexp.MustCompile(`[+\-]?(?:(?:0|[1-9]\d*)(?:\.\d*)?|\.\d+)`) ) // GpsToLatLng returns the GPS latitude and longitude as float point number. diff --git a/internal/meta/json.go b/internal/meta/json.go index bb1314a86..bd1131bdf 100644 --- a/internal/meta/json.go +++ b/internal/meta/json.go @@ -35,17 +35,18 @@ func (data *Data) JSON(jsonName, originalName string) (err error) { return fmt.Errorf("metadata: %s not found", quotedName) } - jsonData, err := os.ReadFile(jsonName) + jsonData, err := os.ReadFile(jsonName) //nolint:gosec // jsonName is resolved path from trusted sidecar discovery if err != nil { return fmt.Errorf("cannot read json file %s", quotedName) } - if bytes.Contains(jsonData, []byte("ExifToolVersion")) { + switch { + case bytes.Contains(jsonData, []byte("ExifToolVersion")): return data.Exiftool(jsonData, originalName) - } else if bytes.Contains(jsonData, []byte("albumData")) { + case bytes.Contains(jsonData, []byte("albumData")): return data.GMeta(jsonData) - } else if bytes.Contains(jsonData, []byte("photoTakenTime")) { + case bytes.Contains(jsonData, []byte("photoTakenTime")): return data.GPhoto(jsonData) } diff --git a/internal/meta/json_exiftool.go b/internal/meta/json_exiftool.go index 31340d7b3..0751cfd7d 100644 --- a/internal/meta/json_exiftool.go +++ b/internal/meta/json_exiftool.go @@ -308,8 +308,8 @@ func (data *Data) Exiftool(jsonData []byte, originalName string) (err error) { // Add nanoseconds to the calculated UTC and local time. if data.TakenAt.Nanosecond() == 0 { if ns := time.Duration(data.TakenNs); ns > 0 && ns <= time.Second { - data.TakenAt.Truncate(time.Second).UTC().Add(ns) - data.TakenAtLocal.Truncate(time.Second).Add(ns) + data.TakenAt = data.TakenAt.Truncate(time.Second).UTC().Add(ns) + data.TakenAtLocal = data.TakenAtLocal.Truncate(time.Second).Add(ns) } } diff --git a/internal/meta/json_motion_test.go b/internal/meta/json_motion_test.go index 82229c9c7..67bc615b3 100644 --- a/internal/meta/json_motion_test.go +++ b/internal/meta/json_motion_test.go @@ -26,8 +26,8 @@ func TestJSON_Motion(t *testing.T) { assert.Equal(t, CodecJpeg, data.Codec) assert.Equal(t, int64(0), data.Duration.Milliseconds()) assert.Equal(t, "0s", data.Duration.String()) - assert.Equal(t, "2018-03-18 19:21:15 +0000 UTC", data.TakenAtLocal.String()) - assert.Equal(t, "2018-03-18 23:21:15 +0000 UTC", data.TakenAt.String()) + assert.Equal(t, "2018-03-18 19:21:15.79694 +0000 UTC", data.TakenAtLocal.String()) + assert.Equal(t, "2018-03-18 23:21:15.79694 +0000 UTC", data.TakenAt.String()) assert.Equal(t, 796940000, data.TakenNs) assert.Equal(t, "America/New_York", data.TimeZone) assert.Equal(t, 3024, data.Width) @@ -57,8 +57,8 @@ func TestJSON_Motion(t *testing.T) { assert.Equal(t, CodecJpeg, data.Codec) assert.Equal(t, int64(0), data.Duration.Milliseconds()) assert.Equal(t, "0s", data.Duration.String()) - assert.Equal(t, "2021-09-17 19:31:36 +0000 UTC", data.TakenAtLocal.String()) - assert.Equal(t, "2021-09-17 23:31:36 +0000 UTC", data.TakenAt.String()) + assert.Equal(t, "2021-09-17 19:31:36.844 +0000 UTC", data.TakenAtLocal.String()) + assert.Equal(t, "2021-09-17 23:31:36.844 +0000 UTC", data.TakenAt.String()) assert.Equal(t, 844000000, data.TakenNs) assert.Equal(t, "America/New_York", data.TimeZone) assert.Equal(t, 4032, data.Width) @@ -88,8 +88,8 @@ func TestJSON_Motion(t *testing.T) { assert.Equal(t, CodecJpeg, data.Codec) assert.Equal(t, int64(0), data.Duration.Milliseconds()) assert.Equal(t, "0s", data.Duration.String()) - assert.Equal(t, "2021-12-27 16:13:22 +0000 UTC", data.TakenAtLocal.String()) - assert.Equal(t, "2021-12-27 15:13:22 +0000 UTC", data.TakenAt.String()) + assert.Equal(t, "2021-12-27 16:13:22.429 +0000 UTC", data.TakenAtLocal.String()) + assert.Equal(t, "2021-12-27 15:13:22.429 +0000 UTC", data.TakenAt.String()) assert.Equal(t, 429000000, data.TakenNs) assert.Equal(t, "Europe/Berlin", data.TimeZone) assert.Equal(t, 4032, data.Width) @@ -119,8 +119,8 @@ func TestJSON_Motion(t *testing.T) { assert.Equal(t, CodecJpeg, data.Codec) assert.Equal(t, int64(0), data.Duration.Milliseconds()) assert.Equal(t, "0s", data.Duration.String()) - assert.Equal(t, "2023-03-29 15:48:43 +0000 UTC", data.TakenAtLocal.String()) - assert.Equal(t, "2023-03-29 14:48:43 +0000 UTC", data.TakenAt.String()) + assert.Equal(t, "2023-03-29 15:48:43.201 +0000 UTC", data.TakenAtLocal.String()) + assert.Equal(t, "2023-03-29 14:48:43.201 +0000 UTC", data.TakenAt.String()) assert.Equal(t, 201000000, data.TakenNs) assert.Equal(t, "Europe/London", data.TimeZone) assert.Equal(t, 4080, data.Width) @@ -150,8 +150,8 @@ func TestJSON_Motion(t *testing.T) { assert.Equal(t, CodecJpeg, data.Codec) assert.Equal(t, int64(0), data.Duration.Milliseconds()) assert.Equal(t, "0s", data.Duration.String()) - assert.Equal(t, "2023-08-22 14:38:03 +0000 UTC", data.TakenAtLocal.String()) - assert.Equal(t, "2023-08-22 14:38:03 +0000 UTC", data.TakenAt.String()) + assert.Equal(t, "2023-08-22 14:38:03.583 +0000 UTC", data.TakenAtLocal.String()) + assert.Equal(t, "2023-08-22 14:38:03.583 +0000 UTC", data.TakenAt.String()) assert.Equal(t, 583000000, data.TakenNs) assert.Equal(t, "UTC+3", data.TimeZone) assert.Equal(t, "+03:00", data.TimeOffset) @@ -309,8 +309,8 @@ func TestJSON_Motion(t *testing.T) { assert.Equal(t, int64(0), data.Duration.Milliseconds()) assert.Equal(t, "0s", data.Duration.String()) assert.Equal(t, 308000000, data.TakenNs) - assert.Equal(t, "2023-04-24 13:03:58 +0000 UTC", data.TakenAtLocal.String()) - assert.Equal(t, "2023-04-24 13:03:58 +0000 UTC", data.TakenAt.String()) + assert.Equal(t, "2023-04-24 13:03:58.308 +0000 UTC", data.TakenAtLocal.String()) + assert.Equal(t, "2023-04-24 13:03:58.308 +0000 UTC", data.TakenAt.String()) assert.Equal(t, "UTC+2", data.TimeZone) assert.Equal(t, "+02:00", data.TimeOffset) assert.Equal(t, 4624, data.Width) diff --git a/internal/meta/json_test.go b/internal/meta/json_test.go index f43d7569e..425636d61 100644 --- a/internal/meta/json_test.go +++ b/internal/meta/json_test.go @@ -255,8 +255,8 @@ func TestJSON(t *testing.T) { assert.Equal(t, "0s", data.Duration.String()) assert.InDelta(t, 52.45969, data.Lat, 0.00001) assert.InDelta(t, 13.321831, data.Lng, 0.00001) - assert.Equal(t, "2020-01-01 16:28:23 +0000 UTC", data.TakenAt.String()) - assert.Equal(t, "2020-01-01 17:28:23 +0000 UTC", data.TakenAtLocal.String()) + assert.Equal(t, "2020-01-01 16:28:23.899614 +0000 UTC", data.TakenAt.String()) + assert.Equal(t, "2020-01-01 17:28:23.899614 +0000 UTC", data.TakenAtLocal.String()) assert.Equal(t, 899614000, data.TakenNs) assert.Equal(t, "Europe/Berlin", data.TimeZone) assert.Equal(t, "Night Shift / Berlin / 2020", data.Title) @@ -361,8 +361,8 @@ func TestJSON(t *testing.T) { assert.Equal(t, "", data.InstanceID) assert.Equal(t, CodecJpeg, data.Codec) assert.Equal(t, "0s", data.Duration.String()) - assert.Equal(t, "2018-12-06 12:32:26 +0000 UTC", data.TakenAtLocal.String()) - assert.Equal(t, "2018-12-06 11:32:26 +0000 UTC", data.TakenAt.String()) + assert.Equal(t, "2018-12-06 12:32:26.6 +0000 UTC", data.TakenAtLocal.String()) + assert.Equal(t, "2018-12-06 11:32:26.6 +0000 UTC", data.TakenAt.String()) assert.Equal(t, "Europe/Berlin", data.TimeZone) assert.Equal(t, 3024, data.Width) assert.Equal(t, 4032, data.Height) @@ -386,8 +386,8 @@ func TestJSON(t *testing.T) { assert.Equal(t, "dafbfeb8-a129-4e7c-9cf0-e7996a701cdb", data.InstanceID) assert.Equal(t, CodecJpeg, data.Codec) assert.Equal(t, "0s", data.Duration.String()) - assert.Equal(t, "2018-12-06 12:32:26 +0000 UTC", data.TakenAtLocal.String()) - assert.Equal(t, "2018-12-06 11:32:26 +0000 UTC", data.TakenAt.String()) + assert.Equal(t, "2018-12-06 12:32:26.6 +0000 UTC", data.TakenAtLocal.String()) + assert.Equal(t, "2018-12-06 11:32:26.6 +0000 UTC", data.TakenAt.String()) assert.Equal(t, "Europe/Berlin", data.TimeZone) assert.Equal(t, 1024, data.Width) assert.Equal(t, 1365, data.Height) @@ -411,8 +411,8 @@ func TestJSON(t *testing.T) { assert.Equal(t, "", data.InstanceID) assert.Equal(t, CodecJpeg, data.Codec) assert.Equal(t, "0s", data.Duration.String()) - assert.Equal(t, "2018-12-06 12:32:26 +0000 UTC", data.TakenAtLocal.String()) - assert.Equal(t, "2018-12-06 11:32:26 +0000 UTC", data.TakenAt.String()) + assert.Equal(t, "2018-12-06 12:32:26.6 +0000 UTC", data.TakenAtLocal.String()) + assert.Equal(t, "2018-12-06 11:32:26.6 +0000 UTC", data.TakenAt.String()) assert.Equal(t, "Europe/Berlin", data.TimeZone) assert.Equal(t, 1125, data.Width) assert.Equal(t, 1500, data.Height) @@ -1184,14 +1184,14 @@ func TestJSON(t *testing.T) { if err != nil { t.Fatal(err) } - //t.Logf("all: %+v", data.json) + // t.Logf("all: %+v", data.json) assert.Equal(t, "creator A, creator B", data.Artist) assert.Equal(t, "my image headline", data.Title) assert.Equal(t, "my iptc description", data.Caption) assert.Equal(t, "my iptc copyright", data.Copyright) - //TODO - //assert.Equal(t, "zqdtcxt1q9wrxnur", data.DocumentID) + // TODO + // assert.Equal(t, "zqdtcxt1q9wrxnur", data.DocumentID) }) t.Run("IPhoneSixSJson", func(t *testing.T) { data, err := JSON("testdata/iPhone_6s.json", "") @@ -1204,8 +1204,8 @@ func TestJSON(t *testing.T) { assert.Equal(t, CodecJpeg, data.Codec) assert.Equal(t, "0s", data.Duration.String()) - assert.Equal(t, "2022-11-02 12:54:16 +0000 UTC", data.TakenAtLocal.String()) - assert.Equal(t, "2022-11-02 11:54:16 +0000 UTC", data.TakenAt.String()) + assert.Equal(t, "2022-11-02 12:54:16.698 +0000 UTC", data.TakenAtLocal.String()) + assert.Equal(t, "2022-11-02 11:54:16.698 +0000 UTC", data.TakenAt.String()) assert.Equal(t, 698000000, data.TakenNs) assert.Equal(t, "UTC+1", data.TimeZone) assert.Equal(t, 4032, data.Width) @@ -1228,8 +1228,8 @@ func TestJSON(t *testing.T) { assert.Equal(t, CodecJpeg, data.Codec) assert.Equal(t, "0s", data.Duration.String()) - assert.Equal(t, "2022-09-23 13:30:04 +0000 UTC", data.TakenAtLocal.String()) - assert.Equal(t, "2022-09-23 12:30:04 +0000 UTC", data.TakenAt.String()) + assert.Equal(t, "2022-09-23 13:30:04.63 +0000 UTC", data.TakenAtLocal.String()) + assert.Equal(t, "2022-09-23 12:30:04.63 +0000 UTC", data.TakenAt.String()) assert.Equal(t, 630000000, data.TakenNs) assert.Equal(t, "UTC+1", data.TimeZone) assert.Equal(t, 4032, data.Width) @@ -1252,8 +1252,8 @@ func TestJSON(t *testing.T) { // t.Logf("DATA: %+v", data) assert.Equal(t, CodecJpeg, data.Codec) - assert.Equal(t, "2022-04-24 10:35:53 +0000 UTC", data.TakenAtLocal.String()) - assert.Equal(t, "2022-04-24 02:35:53 +0000 UTC", data.TakenAt.String()) + assert.Equal(t, "2022-04-24 10:35:53.358 +0000 UTC", data.TakenAtLocal.String()) + assert.Equal(t, "2022-04-24 02:35:53.358 +0000 UTC", data.TakenAt.String()) assert.Equal(t, "Asia/Shanghai", data.TimeZone) // Local Time assert.Equal(t, 1, data.Orientation) assert.Equal(t, float32(33.640007), float32(data.Lat)) @@ -1283,8 +1283,8 @@ func TestJSON(t *testing.T) { // t.Logf("DATA: %#v", data) assert.Equal(t, "IMG_9395.heic", data.FileName) - assert.Equal(t, "2023-10-02 13:20:17 +0000 UTC", data.TakenAtLocal.String()) - assert.Equal(t, "2023-10-02 11:20:17 +0000 UTC", data.TakenAt.String()) + assert.Equal(t, "2023-10-02 13:20:17.568 +0000 UTC", data.TakenAtLocal.String()) + assert.Equal(t, "2023-10-02 11:20:17.568 +0000 UTC", data.TakenAt.String()) assert.Equal(t, 568000000, data.TakenNs) assert.Equal(t, "UTC+2", data.TimeZone) assert.Equal(t, "+02:00", data.TimeOffset) diff --git a/internal/meta/sanitize.go b/internal/meta/sanitize.go index 6fced18b8..de5c4aa56 100644 --- a/internal/meta/sanitize.go +++ b/internal/meta/sanitize.go @@ -62,7 +62,7 @@ var UnwantedStrings = map[string]bool{ } // LowerCaseRegexp matches lower-case tokens in generated filenames. -var LowerCaseRegexp = regexp.MustCompile("[a-z\\d_\\-]+") +var LowerCaseRegexp = regexp.MustCompile(`[a-z\d_\-]+`) // SanitizeUnicode returns the string as valid Unicode with whitespace trimmed. func SanitizeUnicode(s string) string { @@ -85,7 +85,7 @@ func SanitizeString(s string) string { return "" } - return SanitizeUnicode(strings.Replace(s, "\"", "", -1)) + return SanitizeUnicode(strings.ReplaceAll(s, "\"", "")) } // SanitizeUID normalizes unique IDs found in XMP or Exif metadata. diff --git a/internal/meta/xmp_document.go b/internal/meta/xmp_document.go index 3273ba248..da938a975 100644 --- a/internal/meta/xmp_document.go +++ b/internal/meta/xmp_document.go @@ -203,7 +203,7 @@ type XmpDocument struct { // Load parses an XMP file and populates document values with its contents. func (doc *XmpDocument) Load(filename string) error { - data, err := os.ReadFile(filename) + data, err := os.ReadFile(filename) //nolint:gosec // filename is provided by caller; reading sidecar files is expected if err != nil { return err @@ -288,8 +288,5 @@ func (doc *XmpDocument) Keywords() string { // Favorite returns a favorite status in the XMP document. func (doc *XmpDocument) Favorite() bool { fstop := doc.RDF.Description.FStopFavorite - if fstop == "1" { - return true - } - return false + return fstop == "1" } From 162b383f314ee5d19916fdc4f5f56be686e38833 Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sat, 22 Nov 2025 12:48:58 +0100 Subject: [PATCH 025/195] Docs: Add internal/meta/README.md to document the "meta" package Signed-off-by: Michael Mayer --- internal/meta/README.md | 45 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 internal/meta/README.md diff --git a/internal/meta/README.md b/internal/meta/README.md new file mode 100644 index 000000000..e0e1c2e07 --- /dev/null +++ b/internal/meta/README.md @@ -0,0 +1,45 @@ +## PhotoPrism — Metadata Pipeline + +**Last Updated:** November 22, 2025 + +### Overview + +The `internal/meta` package extracts, normalizes, and reports metadata from images, videos, and sidecars (Exif, XMP, JSON). It produces a `meta.Data` struct that downstream components (indexer, UI, API) consume for dates, GPS, camera/lens info, keywords, and motion-photo flags. The package aims to be loss-tolerant (accepts imperfect files), deterministic (stable parsing order), and explicit about fallbacks. + +### Guidelines + +- Keep nanosecond precision in `meta.Data`; adjust consumers/tests instead of truncating here. +- When comparing or persisting times, be aware of second-only storage in entity and DB layers. +- For stacking or dedupe features, use second-based keys unless the DB schema is upgraded. +- When adding new parsers, ensure they fail softly and add test fixtures mirroring real-world oddities. + +### Time & Precision + +- Parsers preserve sub-second timestamps found in Exif/XMP/JSON (`TakenAt`, `TakenAtLocal`, `TakenNs`). Tests expect nanosecond precision where available. +- Downstream persistence truncates to whole seconds: + - `MediaFile.TakenAt()` truncates `meta.Data` timestamps to seconds before caching. + - Entity columns `photos.taken_at`, `photos.taken_at_local`, and `files.photo_taken_at` are `DATETIME` (no fractional seconds). + - YAML metadata backups serialize the entity values, so they also lose sub-second precision. +- Stack/search logic remains second-based (`MapKey` uses `takenAt.Unix()`), so nanoseconds do not affect grouping or comparisons. +- If future work needs sub-second storage, columns must switch to `DATETIME(6)` (or similar) and the truncation in `MediaFile.TakenAt()` removed. + +### Parsing Order & Fallbacks + +- Exif → XMP → JSON (ExifTool/GPhotos/motion) → filename → filesystem mtime. Each stage logs source and errors but continues when safe. +- Brute-force Exif search is used when native parsers fail; errors are logged with context. +- GPS parsing supports decimal and DMS formats; regexes are kept simple and precompiled. + +### Motion Photos & Embedded Media + +- Motion-photo JSON readers set `HasThumbEmbedded` / `HasVideoEmbedded`, `Codec`, `Duration`, and capture accurate timestamps (including ns) when present. +- Time zones from motion metadata are respected; missing zones fall back to UTC. + +### Sanitization + +- `SanitizeString`, `SanitizeUnicode`, and related helpers strip binary markers, quotes, and invalid Unicode; filenames and keywords use lower-case, dash/underscore-safe regexes. +- Lower-case regex and quote removal now use `ReplaceAll` and raw strings to avoid double escaping. + +### Docs & References + +- External tag references are listed in `docs.go`. +- Tests under `internal/meta/testdata` cover Exif, XMP, motion photos, and edge cases (missing headers, panoramas, time offsets). From 2a00122dd15936f4f208d2dbb790121173ee5090 Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sat, 22 Nov 2025 12:51:47 +0100 Subject: [PATCH 026/195] Docs: Add photoprism/dl/README.md Signed-off-by: Michael Mayer --- internal/photoprism/dl/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/photoprism/dl/README.md b/internal/photoprism/dl/README.md index 59af04efe..6cc2ec75d 100644 --- a/internal/photoprism/dl/README.md +++ b/internal/photoprism/dl/README.md @@ -1,4 +1,4 @@ -# PhotoPrism Download Helpers +## PhotoPrism — Download Helpers This package provides thin wrappers around `yt-dlp`, which the `photoprism dl` command uses for metadata discovery and downloading. @@ -6,20 +6,20 @@ It currently supports two invocation methods: - Pipe: stream to stdout, PhotoPrism writes a file and remuxes with ffmpeg to ensure MP4 + embedded metadata. - File: `yt-dlp` writes files to disk using `--output`; PhotoPrism captures final paths via `--print after_move:filepath` and may remux when needed. -## Auth & Headers +### Auth & Headers - Supports `--cookies`, `--cookies-from-browser BROWSER[:PROFILE]`, and repeatable `--add-header` for both metadata and download flows. - Container note: The `photoprism dl` CLI runs in a container by default and therefore does not expose a `--cookies-from-browser` flag (no access to local browser profiles). Use `--cookies ` with a Netscape cookies.txt file. - Secrets are never logged; header values are redacted in trace logs. -## Key APIs +### Key APIs - `NewMetadata(ctx, url, Options)` → discovers formats and info (via `--dump-single-json`). - `Metadata.DownloadWithOptions(ctx, DownloadOptions)` → pipe method (`stdout`). - `Metadata.DownloadToFileWithOptions(ctx, DownloadOptions)` → file method (`--output` + `--print`). - `RemuxOptionsFromInfo(ffmpegBin, fs.VideoMp4, Info, sourceURL)` → builds ffmpeg options to embed title/description/author/comment/created. -## yt-dlp CLI +### yt-dlp CLI - **Format selection** - Default behavior is `bestvideo*+bestaudio/best`, which already prefers the highest quality muxable streams. @@ -36,12 +36,12 @@ It currently supports two invocation methods: - **Other frequently used knobs** - `--download-sections`, `--add-header`, `--cookies`, `--proxy`, `--impersonate` – passed through via `dl.Options` when callers need them. -## Testing +### Testing - Tests stub `yt-dlp` with a tiny shell script that echoes JSON or creates a dummy file and prints its path. This avoids external network calls and brittle extractor behavior. - Logging redaction is covered; argument construction is verified for cookies/headers. -## Notes +### Notes - Prefer the file method for sources with separate audio/video streams; the pipe method cannot always merge in that case. - When the CLI’s `--file-remux=auto` is used, the final ffmpeg remux is skipped for MP4 outputs that already include metadata. From 153ebd59052306ef6e42f1b64291709dea71fbde Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sat, 22 Nov 2025 12:52:15 +0100 Subject: [PATCH 027/195] Docs: Update photoprism/dl/README.md Signed-off-by: Michael Mayer --- internal/photoprism/dl/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/photoprism/dl/README.md b/internal/photoprism/dl/README.md index 6cc2ec75d..7f5ed8e62 100644 --- a/internal/photoprism/dl/README.md +++ b/internal/photoprism/dl/README.md @@ -1,5 +1,9 @@ ## PhotoPrism — Download Helpers +**Last Updated:** November 22, 2025 + +### Overview + This package provides thin wrappers around `yt-dlp`, which the `photoprism dl` command uses for metadata discovery and downloading. It currently supports two invocation methods: From 43bca10b4e986e12fd330266ca8e3fa2de07045a Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sat, 22 Nov 2025 12:58:11 +0100 Subject: [PATCH 028/195] CI: Apply Go linter recommendations to "internal/ffmpeg" package #5330 Signed-off-by: Michael Mayer --- internal/ffmpeg/README.md | 70 ++++++++++++++++++++++++++++ internal/ffmpeg/apple/avc.go | 1 + internal/ffmpeg/encode/avc.go | 1 + internal/ffmpeg/encode/flags.go | 2 +- internal/ffmpeg/encode/options.go | 21 +++++---- internal/ffmpeg/extract_image_cmd.go | 2 + internal/ffmpeg/intel/avc.go | 2 + internal/ffmpeg/nvidia/avc.go | 1 + internal/ffmpeg/remux.go | 13 +++--- internal/ffmpeg/test.go | 1 + internal/ffmpeg/transcode_cmd.go | 1 + internal/ffmpeg/v4l/avc.go | 1 + internal/ffmpeg/vaapi/avc.go | 2 + 13 files changed, 102 insertions(+), 16 deletions(-) create mode 100644 internal/ffmpeg/README.md diff --git a/internal/ffmpeg/README.md b/internal/ffmpeg/README.md new file mode 100644 index 000000000..2a4180ce8 --- /dev/null +++ b/internal/ffmpeg/README.md @@ -0,0 +1,70 @@ +## PhotoPrism — FFmpeg Integration + +**Last Updated:** November 22, 2025 + +### Overview + +`internal/ffmpeg` wraps the `ffmpeg` CLI to transcode videos to AVC/H.264, remux containers, and extract preview frames in a predictable, testable way. Command builders share option structs so CLI tools, workers, and tests can select software or hardware encoders without duplicating flag logic. + +#### Context & Constraints + +- Relies on the system `ffmpeg` binary; defaults to `FFmpegBin` but callers may override `Options.Bin`. +- Inputs are internal filenames and option structs (not user input); exec invocations are annotated with `#nosec G204`. +- Downstream jobs may run concurrently, so `TranscodeCmd` returns a `useMutex` hint to serialize expensive work. +- Remux and extract commands honor `Force` and reuse shared map flags; metadata copying is limited to safe defaults. + +#### Goals + +- Provide consistent command lines for software and hardware AVC encoders. +- Keep remuxing and preview extraction lightweight while preserving metadata where possible. +- Centralize quality and size clamping logic so UIs/CLI can pass user preferences safely. + +#### Non-Goals + +- Full coverage of every FFmpeg codec or container; the package focuses on MP4/H.264 paths required by PhotoPrism. +- Direct management of FFmpeg installation or GPU availability. + +### Encoders, Containers, & Hardware + +- **Software AVC:** `encode.TranscodeToAvcCmd` (x264 or default encoder). +- **Intel Quick Sync:** `internal/ffmpeg/intel` (`h264_qsv`) with optional `Options.Device`. +- **NVIDIA NVENC:** `internal/ffmpeg/nvidia` (`h264_nvenc`). +- **Apple VideoToolbox:** `internal/ffmpeg/apple` (`h264_videotoolbox`). +- **VA-API:** `internal/ffmpeg/vaapi` (`h264_vaapi`) supporting optional device paths. +- **V4L2 M2M:** `internal/ffmpeg/v4l` (`h264_v4l2m2m`) for ARM/embedded targets. +- **Containers:** MP4 is the primary target (`fs.VideoMp4`); `RemuxCmd` can handle other `fs.Type` values when provided. +- **Streaming flags:** `encode.MovFlags` defaults to `use_metadata_tags+faststart` to keep outputs stream-friendly. + +### Package Layout (Code Map) + +- `encode/` — shared option structs, quality helpers, default map/metadata flags, software AVC command builder. +- `apple/`, `intel/`, `nvidia/`, `vaapi/`, `v4l/` — hardware-specific AVC command builders. +- `remux.go` — container-only transfers with metadata copy and temp-file safety. +- `transcode_cmd.go` — selects encoder, handles animated image inputs, and signals mutex usage. +- `extract_image_cmd.go` — JPEG/PNG preview frame extraction with color-space presets. +- `test.go` & `*_test.go` — reusable command runner and smoke tests (use fixtures in `testdata/`). +- `ffmpeg.go` — package logger hook. + +### Related Packages & Entry Points + +- `internal/thumb` calls these builders for video previews and thumbnails. +- `internal/commands` and workers select encoders based on configuration options and reuse `encode.Options`. +- `pkg/fs` supplies path helpers, existence checks, and file-mode constants referenced by remux/extract logic. + +### Configuration & Safety Notes + +- Clamp size and quality via `NewVideoOptions` to `[1, 15360]` pixels and the defined quality bounds. +- Remuxing respects `Options.Force`; without it existing outputs are preserved. +- Metadata copying uses `-map_metadata` and `clean` sanitizers; only safe string fields (title, description, comment, author, creation_time) are added when set. +- Hardware helpers expect the matching FFmpeg build and devices; callers should gate selection via config or environment (see `PHOTOPRISM_FFMPEG_ENCODER` guidance in `AGENTS.md`). + +### Testing + +- Run unit tests: `go test ./internal/ffmpeg/...` +- Hardware-specific tests assume the encoder is available; keep runs gated via config when adding new cases. + +### Operational Tips + +- Prefer `TranscodeCmd` over manual `exec.Command` to keep logging, metadata, and mutex hints consistent. +- Use `RemuxFile` to convert containers without re-encoding; it creates a temp file and swaps atomically. +- For preview frames, pass `encode.Options` with `SeekOffset` and `TimeOffset` computed from video duration (see `NewPreviewImageOptions`). diff --git a/internal/ffmpeg/apple/avc.go b/internal/ffmpeg/apple/avc.go index 12a4b21f8..ead685e2e 100644 --- a/internal/ffmpeg/apple/avc.go +++ b/internal/ffmpeg/apple/avc.go @@ -8,6 +8,7 @@ import ( // TranscodeToAvcCmd returns the FFmpeg command for hardware-accelerated transcoding to MPEG-4 AVC. func TranscodeToAvcCmd(srcName, destName string, opt encode.Options) *exec.Cmd { + // #nosec G204 -- command arguments are built from validated options and paths. return exec.Command( opt.Bin, "-hide_banner", diff --git a/internal/ffmpeg/encode/avc.go b/internal/ffmpeg/encode/avc.go index 85ee88f2d..a44752781 100644 --- a/internal/ffmpeg/encode/avc.go +++ b/internal/ffmpeg/encode/avc.go @@ -4,6 +4,7 @@ import "os/exec" // TranscodeToAvcCmd returns the default FFmpeg command for transcoding video files to MPEG-4 AVC. func TranscodeToAvcCmd(srcName, destName string, opt Options) *exec.Cmd { + // #nosec G204 -- command arguments are built from validated options and paths. return exec.Command( opt.Bin, "-hide_banner", diff --git a/internal/ffmpeg/encode/flags.go b/internal/ffmpeg/encode/flags.go index da2a8462c..dce5fe982 100644 --- a/internal/ffmpeg/encode/flags.go +++ b/internal/ffmpeg/encode/flags.go @@ -1,6 +1,6 @@ package encode -// The MovFlags default forces fragmented MP4 output suitable for streaming: +// MovFlags defines default fragmented MP4 flags suitable for streaming: // - https://developer.mozilla.org/en-US/docs/Web/API/Media_Source_Extensions_API/Transcoding_assets_for_MSE#fragmenting // - https://nschlia.github.io/ffmpegfs/html/ffmpeg__profiles_8cc.html // - https://cloudinary.com/glossary/fragmented-mp4 diff --git a/internal/ffmpeg/encode/options.go b/internal/ffmpeg/encode/options.go index 2da4f850d..9f22cbe7e 100644 --- a/internal/ffmpeg/encode/options.go +++ b/internal/ffmpeg/encode/options.go @@ -41,17 +41,19 @@ func NewVideoOptions(ffmpegBin string, encoder Encoder, sizeLimit, quality int, encoder = DefaultAvcEncoder() } - if sizeLimit < 1 { + switch { + case sizeLimit < 1: sizeLimit = 1920 - } else if sizeLimit > 15360 { + case sizeLimit > 15360: sizeLimit = 15360 } - if quality <= 0 { + switch { + case quality <= 0: quality = DefaultQuality - } else if quality < WorstQuality { + case quality < WorstQuality: quality = WorstQuality - } else if quality >= BestQuality { + case quality >= BestQuality: quality = BestQuality } @@ -118,13 +120,14 @@ func NewPreviewImageOptions(ffmpegBin string, videoDuration time.Duration) *Opti // VideoFilter returns the FFmpeg video filter string based on the size limit in pixels and the pixel format. func (o *Options) VideoFilter(format PixelFormat) string { // scale specifies the FFmpeg downscale filter, see http://trac.ffmpeg.org/wiki/Scaling. - if format == "" { + switch format { + case "": return fmt.Sprintf("scale='if(gte(iw,ih), min(%d, iw), -2):if(gte(iw,ih), -2, min(%d, ih))'", o.SizeLimit, o.SizeLimit) - } else if format == FormatQSV { + case FormatQSV: return fmt.Sprintf("scale_qsv=w='if(gte(iw,ih), min(%d, iw), -1)':h='if(gte(iw,ih), -1, min(%d, ih))':format=nv12", o.SizeLimit, o.SizeLimit) - } else { - return fmt.Sprintf("scale='if(gte(iw,ih), min(%d, iw), -2):if(gte(iw,ih), -2, min(%d, ih))',format=%s", o.SizeLimit, o.SizeLimit, format) } + + return fmt.Sprintf("scale='if(gte(iw,ih), min(%d, iw), -2):if(gte(iw,ih), -2, min(%d, ih))',format=%s", o.SizeLimit, o.SizeLimit, format) } // QvQuality returns the video encoding quality as "-q:v" parameter string. diff --git a/internal/ffmpeg/extract_image_cmd.go b/internal/ffmpeg/extract_image_cmd.go index 01a85ae74..dfd1cc1d6 100644 --- a/internal/ffmpeg/extract_image_cmd.go +++ b/internal/ffmpeg/extract_image_cmd.go @@ -26,6 +26,7 @@ func ExtractJpegImageCmd(videoName, imageName string, opt *encode.Options) *exec // see https://github.com/photoprism/photoprism/issues/4488. // Unfortunately, this filter would render thumbnails of non-HDR videos too dark: // "-vf", "zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=tonemap=gamma:desat=0,zscale=t=bt709:m=bt709:r=tv,format=yuv420p", + // #nosec G204 -- paths and flags are created by the application, not user input. return exec.Command( opt.Bin, "-hide_banner", @@ -46,6 +47,7 @@ func ExtractJpegImageCmd(videoName, imageName string, opt *encode.Options) *exec // ExtractPngImageCmd extracts a PNG still image from the specified source video file. func ExtractPngImageCmd(videoName, imageName string, opt *encode.Options) *exec.Cmd { + // #nosec G204 -- paths and flags are created by the application, not user input. return exec.Command( opt.Bin, "-hide_banner", diff --git a/internal/ffmpeg/intel/avc.go b/internal/ffmpeg/intel/avc.go index 67c903482..18f2b7db9 100644 --- a/internal/ffmpeg/intel/avc.go +++ b/internal/ffmpeg/intel/avc.go @@ -10,6 +10,7 @@ import ( func TranscodeToAvcCmd(srcName, destName string, opt encode.Options) *exec.Cmd { // ffmpeg -hide_banner -h encoder=h264_qsv if opt.Device != "" { + // #nosec G204 -- command arguments are built from validated options and paths. return exec.Command( opt.Bin, "-hide_banner", @@ -33,6 +34,7 @@ func TranscodeToAvcCmd(srcName, destName string, opt encode.Options) *exec.Cmd { destName, ) } else { + // #nosec G204 -- command arguments are built from validated options and paths. return exec.Command( opt.Bin, "-hide_banner", diff --git a/internal/ffmpeg/nvidia/avc.go b/internal/ffmpeg/nvidia/avc.go index 48eb2a112..add81bfde 100644 --- a/internal/ffmpeg/nvidia/avc.go +++ b/internal/ffmpeg/nvidia/avc.go @@ -9,6 +9,7 @@ import ( // TranscodeToAvcCmd returns the FFmpeg command for hardware-accelerated transcoding to MPEG-4 AVC. func TranscodeToAvcCmd(srcName, destName string, opt encode.Options) *exec.Cmd { // ffmpeg -hide_banner -h encoder=h264_nvenc + // #nosec G204 -- command arguments are built from validated options and paths. return exec.Command( opt.Bin, "-hide_banner", diff --git a/internal/ffmpeg/remux.go b/internal/ffmpeg/remux.go index 2ef055fa5..4cac62906 100644 --- a/internal/ffmpeg/remux.go +++ b/internal/ffmpeg/remux.go @@ -131,13 +131,14 @@ func RemuxFile(videoFilePath, destFilePath string, opt encode.Options) error { // RemuxCmd returns the FFmpeg command for transferring content from one container format to another without altering the original video or audio stream. func RemuxCmd(srcName, destName string, opt encode.Options) (cmd *exec.Cmd, err error) { - if srcName == "" { + switch { + case srcName == "": return nil, fmt.Errorf("empty source filename") - } else if !fs.FileExistsNotEmpty(srcName) { + case !fs.FileExistsNotEmpty(srcName): return nil, fmt.Errorf("source file is empty or missing") - } else if destName == "" { + case destName == "": return nil, fmt.Errorf("empty destination filename") - } else if srcName == destName { + case srcName == destName: return nil, fmt.Errorf("source and destination filenames must be different") } @@ -164,8 +165,7 @@ func RemuxCmd(srcName, destName string, opt encode.Options) (cmd *exec.Cmd, err } // Append format specific "ffmpeg" command flags. - switch opt.Container { - case fs.VideoMp4: + if opt.Container == fs.VideoMp4 { // Ensure MP4 compatibility: flags = append(flags, "-movflags", opt.MovFlags, @@ -197,6 +197,7 @@ func RemuxCmd(srcName, destName string, opt encode.Options) (cmd *exec.Cmd, err // Set the destination file name as the last command flag. flags = append(flags, destName) + // #nosec G204 -- filenames and flags are constructed internally and not user-controlled. cmd = exec.Command( opt.Bin, flags..., diff --git a/internal/ffmpeg/test.go b/internal/ffmpeg/test.go index 0c407ac36..dcaacab5c 100644 --- a/internal/ffmpeg/test.go +++ b/internal/ffmpeg/test.go @@ -13,6 +13,7 @@ import ( "github.com/photoprism/photoprism/pkg/fs" ) +// RunCommandTest executes ffmpeg command tests and cleans up created files. func RunCommandTest(t *testing.T, encoder encode.Encoder, srcName, destName string, cmd *exec.Cmd, deleteAfterTest bool) { var out bytes.Buffer var stderr bytes.Buffer diff --git a/internal/ffmpeg/transcode_cmd.go b/internal/ffmpeg/transcode_cmd.go index 5453ffa40..c85407bad 100644 --- a/internal/ffmpeg/transcode_cmd.go +++ b/internal/ffmpeg/transcode_cmd.go @@ -31,6 +31,7 @@ func TranscodeCmd(srcName, destName string, opt encode.Options) (cmd *exec.Cmd, // Always use software encoder for transcoding animated pictures into videos. if fs.TypeAnimated[fs.FileType(srcName)] != "" { + // #nosec G204 -- command arguments are built from validated options and paths. cmd = exec.Command( opt.Bin, "-hide_banner", diff --git a/internal/ffmpeg/v4l/avc.go b/internal/ffmpeg/v4l/avc.go index 408514f42..a218d56fe 100644 --- a/internal/ffmpeg/v4l/avc.go +++ b/internal/ffmpeg/v4l/avc.go @@ -9,6 +9,7 @@ import ( // TranscodeToAvcCmd returns the FFmpeg command for hardware-accelerated transcoding to MPEG-4 AVC. func TranscodeToAvcCmd(srcName, destName string, opt encode.Options) *exec.Cmd { // ffmpeg -hide_banner -h encoder=h264_v4l2m2m + // #nosec G204 -- command arguments are built from validated options and paths. return exec.Command( opt.Bin, "-hide_banner", diff --git a/internal/ffmpeg/vaapi/avc.go b/internal/ffmpeg/vaapi/avc.go index 75a38e8ac..55b217a32 100644 --- a/internal/ffmpeg/vaapi/avc.go +++ b/internal/ffmpeg/vaapi/avc.go @@ -9,6 +9,7 @@ import ( // TranscodeToAvcCmd returns the FFmpeg command for hardware-accelerated transcoding to MPEG-4 AVC. func TranscodeToAvcCmd(srcName, destName string, opt encode.Options) *exec.Cmd { if opt.Device != "" { + // #nosec G204 -- command arguments are built from validated options and paths. return exec.Command( opt.Bin, "-hide_banner", @@ -30,6 +31,7 @@ func TranscodeToAvcCmd(srcName, destName string, opt encode.Options) *exec.Cmd { destName, ) } else { + // #nosec G204 -- command arguments are built from validated options and paths. return exec.Command( opt.Bin, "-hide_banner", From e693fd668f58234e2373af3bbe61c2c886824f62 Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sat, 22 Nov 2025 13:09:32 +0100 Subject: [PATCH 029/195] CI: Apply Go linter recommendations to "internal/server" package #5330 Signed-off-by: Michael Mayer --- internal/server/README.md | 61 ++++++++++++++++++++++++++ internal/server/recovery.go | 3 +- internal/server/routes_webapp.go | 1 + internal/server/start.go | 14 ++++-- internal/server/webdav_actions_test.go | 2 + internal/server/webdav_write_test.go | 15 ++++--- 6 files changed, 85 insertions(+), 11 deletions(-) create mode 100644 internal/server/README.md diff --git a/internal/server/README.md b/internal/server/README.md new file mode 100644 index 000000000..42338b89f --- /dev/null +++ b/internal/server/README.md @@ -0,0 +1,61 @@ +## PhotoPrism — HTTP Server + +**Last Updated:** November 22, 2025 + +### Overview + +`internal/server` wires Gin, middleware, and configuration into the PhotoPrism HTTP/HTTPS/WebDAV servers. It owns startup/shutdown orchestration, route registration, and helpers for recovery/logging. Subpackages (`process`, `limits`, etc.) are kept lightweight so CLI commands and workers can embed the same server behavior without duplicating boilerplate. + +#### Context & Constraints + +- Uses the configured `config.Config` to decide TLS, AutoTLS, Unix sockets, proxies, compression, and trusted headers. +- Middleware must stay small and deterministic because it runs on every request; heavy logic belongs in handlers. +- Panics are recovered by `Recovery()` which logs stack traces and returns 500. +- Startup supports mutually exclusive endpoints: Unix socket, HTTPS with certs, AutoTLS (with redirect listener), or plain HTTP. + +#### Goals + +- Provide a single entrypoint (`Start`) that configures listeners, middleware, and routes consistently. +- Keep health/readiness endpoints lightweight and cache-safe. +- Ensure redirect and TLS listeners include sensible timeouts. + +#### Non-Goals + +- Managing Docker/Traefik lifecycle (handled by compose files). +- Serving static files directly; templates are loaded via Gin and routed by `routes_webapp.go`. + +### Package Layout (Code Map) + +- `start.go` — main startup flow, listener selection (HTTP/HTTPS/AutoTLS/Unix socket), graceful shutdown. +- `routes_webapp.go` — Web UI routes and shared method helpers (`MethodsGetHead`). +- `recovery.go` — panic recovery middleware with stack trace logging. +- `logger.go` — request logging middleware (enabled in debug mode). +- `security.go` — security headers and trusted proxy/platform handling. +- `webdav_*.go` & tests — WebDAV handlers and regression tests for overwrite, traversal, and metadata flags. +- `process/` — light wrappers for server process metadata. + +### Related Packages + +- `internal/api` — registers REST endpoints consumed by `registerRoutes`. +- `internal/config` — supplies HTTP/TLS/socket settings, compression, proxies, and base URI paths. +- `internal/server/process` — exposes process ID for logging. +- `pkg/http/header` — shared HTTP header constants used by health endpoints. + +### Configuration & Safety Notes + +- Compression: only gzip is enabled; brotli requests log a notice. +- Trusted proxies/platform headers are read from config; misconfiguration may expose client IP spoofing—keep the list tight. +- AutoTLS: uses `autocert` and spins up a redirect listener with explicit read/write timeouts; ensure ports 80/443 are reachable. +- Unix sockets: optional `force` query removes stale sockets; permissions can be set via `mode` query. +- Health endpoints (`/livez`, `/health`, `/healthz`, `/readyz`) return `Cache-Control: no-store` and `Access-Control-Allow-Origin: *`. + +### Testing + +- Lint & unit tests: `golangci-lint run ./internal/server...` and `go test ./internal/server/...` +- WebDAV behaviors are covered by `webdav_*_test.go`; they rely on temp directories and in-memory routers. + +### Operational Tips + +- Prefer `Start` with context cancellation so graceful shutdown is triggered (`server.Close()`). +- When adding routes, register them in `registerRoutes` and reuse `MethodsGetHead` for safe verbs. +- Keep middleware light; log or enforce security at the edge (Traefik) when possible, but maintain server-side defaults for defense in depth. diff --git a/internal/server/recovery.go b/internal/server/recovery.go index b736fc48f..029d91e9d 100644 --- a/internal/server/recovery.go +++ b/internal/server/recovery.go @@ -48,6 +48,7 @@ func stack(skip int) []byte { // Print this much at least. If we cannot find the source, it won't show. fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc) if file != lastFile { + // #nosec G304 -- file path comes from runtime.Caller and is limited to source files. data, err := os.ReadFile(file) if err != nil { continue @@ -90,6 +91,6 @@ func function(pc uintptr) []byte { if period := bytes.Index(name, dot); period >= 0 { name = name[period+1:] } - name = bytes.Replace(name, centerDot, dot, -1) + name = bytes.ReplaceAll(name, centerDot, dot) return name } diff --git a/internal/server/routes_webapp.go b/internal/server/routes_webapp.go index bd04186d2..5cafb713a 100644 --- a/internal/server/routes_webapp.go +++ b/internal/server/routes_webapp.go @@ -13,6 +13,7 @@ import ( "github.com/photoprism/photoprism/pkg/i18n" ) +// MethodsGetHead enumerates the safe GET/HEAD methods used by web app routes. var MethodsGetHead = []string{http.MethodGet, http.MethodHead} // registerWebAppRoutes adds routes for the web user interface. diff --git a/internal/server/start.go b/internal/server/start.go index 1359c3ca5..eadb88ffc 100644 --- a/internal/server/start.go +++ b/internal/server/start.go @@ -41,7 +41,7 @@ func Start(ctx context.Context, conf *config.Config) { // Set web server mode. if conf.HttpMode() != "" { gin.SetMode(conf.HttpMode()) - } else if conf.Debug() == false { + } else if !conf.Debug() { gin.SetMode(gin.ReleaseMode) } @@ -154,7 +154,7 @@ func Start(ctx context.Context, conf *config.Config) { // Check if the Unix socket already exists and delete it if the force flag is set. if fs.SocketExists(unixSocket.Path) { - if txt.Bool(unixSocket.Query().Get("force")) == false { + if !txt.Bool(unixSocket.Query().Get("force")) { Fail("server: %s socket %s already exists", clean.Log(unixSocket.Scheme), clean.Log(unixSocket.Path)) return } else if removeErr := os.Remove(unixSocket.Path); removeErr != nil { @@ -278,7 +278,15 @@ func StartAutoTLS(s *http.Server, m *autocert.Manager, conf *config.Config) { var g errgroup.Group g.Go(func() error { - return http.ListenAndServe(fmt.Sprintf("%s:%d", conf.HttpHost(), conf.HttpPort()), m.HTTPHandler(http.HandlerFunc(redirect))) + redirectSrv := &http.Server{ + Addr: fmt.Sprintf("%s:%d", conf.HttpHost(), conf.HttpPort()), + Handler: m.HTTPHandler(http.HandlerFunc(redirect)), + ReadHeaderTimeout: time.Minute, + ReadTimeout: 5 * time.Second, + WriteTimeout: 10 * time.Second, + } + + return redirectSrv.ListenAndServe() }) g.Go(func() error { diff --git a/internal/server/webdav_actions_test.go b/internal/server/webdav_actions_test.go index bb12a24cc..e79cb181d 100644 --- a/internal/server/webdav_actions_test.go +++ b/internal/server/webdav_actions_test.go @@ -18,8 +18,10 @@ func TestWebDAVSetFavoriteFlag_CreatesYamlOnce(t *testing.T) { yml := filepath.Join(filepath.Dir(file), "img.yml") assert.FileExists(t, yml) // Write a marker and ensure second call doesn't overwrite content + // #nosec G304 -- test reads file created in a temp directory. orig, _ := os.ReadFile(yml) WebDAVSetFavoriteFlag(file) + // #nosec G304 -- test reads file created in a temp directory. now, _ := os.ReadFile(yml) assert.Equal(t, string(orig), string(now)) } diff --git a/internal/server/webdav_write_test.go b/internal/server/webdav_write_test.go index 63b26a48b..6155053fc 100644 --- a/internal/server/webdav_write_test.go +++ b/internal/server/webdav_write_test.go @@ -58,6 +58,7 @@ func TestWebDAVWrite_MKCOL_PUT(t *testing.T) { assert.InDelta(t, 201, w.Code, 1) // file exists path := filepath.Join(conf.OriginalsPath(), "wdvdir", "hello.txt") + // #nosec G304 -- test reads file created under controlled temp directory. b, err := os.ReadFile(path) assert.NoError(t, err) assert.Equal(t, "hello", string(b)) @@ -147,7 +148,7 @@ func TestWebDAVWrite_OverwriteSemantics(t *testing.T) { authBasic(req) r.ServeHTTP(w, req) // Success (201/204 acceptable) - if !(w.Code == 201 || w.Code == 204) { + if w.Code != http.StatusCreated && w.Code != http.StatusNoContent { t.Fatalf("expected success for Overwrite=T, got %d", w.Code) } b, _ = os.ReadFile(filepath.Join(conf.OriginalsPath(), "dst", "f.txt")) @@ -172,7 +173,7 @@ func TestWebDAVWrite_OverwriteSemantics(t *testing.T) { req.Header.Set("Overwrite", "T") authBasic(req) r.ServeHTTP(w, req) - if !(w.Code == 201 || w.Code == 204) { + if w.Code != http.StatusCreated && w.Code != http.StatusNoContent { t.Fatalf("expected success for MOVE Overwrite=T, got %d", w.Code) } assert.NoFileExists(t, filepath.Join(conf.OriginalsPath(), "src", "g.txt")) @@ -196,7 +197,7 @@ func TestWebDAVWrite_MoveMissingDestination(t *testing.T) { authBasic(req) r.ServeHTTP(w, req) // Expect failure (not 201/204) - if w.Code == 201 || w.Code == 204 { + if w.Code == http.StatusCreated || w.Code == http.StatusNoContent { t.Fatalf("expected failure when Destination header missing, got %d", w.Code) } // Source remains @@ -220,7 +221,7 @@ func TestWebDAVWrite_CopyInvalidDestinationPrefix(t *testing.T) { authBasic(req) r.ServeHTTP(w, req) // Expect failure - if w.Code == 201 || w.Code == 204 { + if w.Code == http.StatusCreated || w.Code == http.StatusNoContent { t.Fatalf("expected failure for invalid Destination prefix, got %d", w.Code) } // Destination not created @@ -242,7 +243,7 @@ func TestWebDAVWrite_MoveNonExistentSource(t *testing.T) { authBasic(req) r.ServeHTTP(w, req) // Expect failure (e.g., 404) - if w.Code == 201 || w.Code == 204 { + if w.Code == http.StatusCreated || w.Code == http.StatusNoContent { t.Fatalf("expected failure moving non-existent source, got %d", w.Code) } assert.NoFileExists(t, filepath.Join(conf.OriginalsPath(), "dst2", "file.txt")) @@ -270,7 +271,7 @@ func TestWebDAVWrite_CopyTraversalDestination(t *testing.T) { authBasic(req) r.ServeHTTP(w, req) // Expect success with sanitized destination inside base - if !(w.Code == 201 || w.Code == 204) { + if w.Code != http.StatusCreated && w.Code != http.StatusNoContent { t.Fatalf("expected success (sanitized), got %d", w.Code) } // Not created above originals; created as /originals/evil.txt @@ -300,7 +301,7 @@ func TestWebDAVWrite_MoveTraversalDestination(t *testing.T) { req.Header.Set("Destination", conf.BaseUri(WebDAVOriginals)+"/../evil2.txt") authBasic(req) r.ServeHTTP(w, req) - if !(w.Code == 201 || w.Code == 204) { + if w.Code != http.StatusCreated && w.Code != http.StatusNoContent { t.Fatalf("expected success (sanitized) for MOVE, got %d", w.Code) } // Source removed; destination created inside base, not outside From c80cf0779f200bb623f74a48e5ff0243fdf3530b Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sat, 22 Nov 2025 13:12:38 +0100 Subject: [PATCH 030/195] Tests: Update mediafile_heic_test.go after fix in "internal/meta" #5330 Signed-off-by: Michael Mayer --- internal/photoprism/mediafile_heic_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/photoprism/mediafile_heic_test.go b/internal/photoprism/mediafile_heic_test.go index 9de5a545a..c310bd004 100644 --- a/internal/photoprism/mediafile_heic_test.go +++ b/internal/photoprism/mediafile_heic_test.go @@ -56,8 +56,8 @@ func TestMediaFile_Heic(t *testing.T) { assert.Nil(t, err) assert.Equal(t, "", jpegInfo.DocumentID) - assert.Equal(t, "2018-09-10 03:16:13 +0000 UTC", jpegInfo.TakenAt.String()) - assert.Equal(t, "2018-09-10 12:16:13 +0000 UTC", jpegInfo.TakenAtLocal.String()) + assert.Equal(t, "2018-09-10 03:16:13.023 +0000 UTC", jpegInfo.TakenAt.String()) + assert.Equal(t, "2018-09-10 12:16:13.023 +0000 UTC", jpegInfo.TakenAtLocal.String()) // KNOWN ISSUE: Orientation 6 would be correct instead (or the image should already be rotated), // see https://github.com/strukturag/libheif/issues/227#issuecomment-1532842570 assert.Equal(t, 1, jpegInfo.Orientation) @@ -119,8 +119,8 @@ func TestMediaFile_Heic(t *testing.T) { assert.Nil(t, err) assert.Equal(t, "", jpegInfo.DocumentID) - assert.Equal(t, "2023-10-31 10:44:43 +0000 UTC", jpegInfo.TakenAt.String()) - assert.Equal(t, "2023-10-31 11:44:43 +0000 UTC", jpegInfo.TakenAtLocal.String()) + assert.Equal(t, "2023-10-31 10:44:43.432 +0000 UTC", jpegInfo.TakenAt.String()) + assert.Equal(t, "2023-10-31 11:44:43.432 +0000 UTC", jpegInfo.TakenAtLocal.String()) assert.Equal(t, 1, jpegInfo.Orientation) assert.Equal(t, "iPhone 15 Pro", jpegInfo.CameraModel) assert.Equal(t, "Apple", jpegInfo.CameraMake) From 9334a9a201df7c195ef5f24b055761828875ae04 Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sat, 22 Nov 2025 14:02:40 +0100 Subject: [PATCH 031/195] CI: Apply Go linter recommendations to "photoprism" package #5330 Signed-off-by: Michael Mayer --- internal/photoprism/README.md | 56 +++++++++++++++ internal/photoprism/cleanup_options.go | 2 +- internal/photoprism/colors.go | 13 ++-- internal/photoprism/colors_test.go | 20 ++++-- internal/photoprism/config.go | 2 +- internal/photoprism/convert_fix.go | 8 ++- internal/photoprism/convert_image.go | 29 ++++---- internal/photoprism/convert_image_jpeg.go | 16 ++++- internal/photoprism/convert_image_png.go | 14 +++- internal/photoprism/convert_sidecar_json.go | 5 +- internal/photoprism/convert_video_avc.go | 1 + internal/photoprism/faces_audit.go | 64 +++++++++-------- internal/photoprism/faces_audit_test.go | 4 +- .../photoprism/faces_cluster_bench_test.go | 2 +- internal/photoprism/faces_match.go | 5 +- internal/photoprism/faces_match_bench_test.go | 2 +- internal/photoprism/faces_optimize.go | 6 +- internal/photoprism/import_worker.go | 10 +-- internal/photoprism/index.go | 49 +++++-------- internal/photoprism/index_faces.go | 4 +- internal/photoprism/index_main.go | 6 +- internal/photoprism/index_mediafile.go | 58 +++++++++------- internal/photoprism/index_mediafile_test.go | 5 +- internal/photoprism/index_related.go | 11 +-- internal/photoprism/label.go | 13 ++-- internal/photoprism/mediafile.go | 68 +++++++++++-------- internal/photoprism/mediafile_fs_test.go | 1 + internal/photoprism/mediafile_heic_test.go | 12 ++-- internal/photoprism/mediafile_meta_test.go | 18 +++-- internal/photoprism/mediafile_related.go | 23 +++---- internal/photoprism/mediafile_test.go | 36 +++++----- internal/photoprism/mediafile_thumbs.go | 5 +- internal/photoprism/mediafile_vision.go | 7 +- internal/photoprism/purge_options.go | 2 +- 34 files changed, 348 insertions(+), 229 deletions(-) create mode 100644 internal/photoprism/README.md diff --git a/internal/photoprism/README.md b/internal/photoprism/README.md new file mode 100644 index 000000000..b170cbfe6 --- /dev/null +++ b/internal/photoprism/README.md @@ -0,0 +1,56 @@ +## PhotoPrism — Core Package + +**Last Updated:** November 22, 2025 + +### Overview + +`internal/photoprism` contains the core application logic for scanning originals, extracting metadata, generating thumbnails, importing/stacking media, and orchestrating converters (FFmpeg/ImageMagick/ExifTool). It wires configuration, indexer, converters, files/photos repositories, and background workers into a single package that other layers (CLI, API, workers) call. + +#### Goals + +- Provide a single, tested entrypoint for indexing/importing media files (`Index`, `IndexMain`, `ImportWorker`). +- Normalize metadata (including sub-second timestamps) before persisting to entities and sidecars. +- Keep converters and thumbnails consistent across CLI, background jobs, and tests. + +#### Non-Goals + +- Direct HTTP handling (lives in `internal/server`). +- Database models (lives in `internal/entity`). +- UI concerns (handled by frontend/JS). + +### Package Layout (Code Map) + +- Indexing/import: `index.go`, `index_main.go`, `index_mediafile.go`, `index_related.go`, `import_worker.go`, `files.go`, `photos.go`. +- Media files & helpers: `mediafile*.go`, `mediafile_thumbs.go`, `mediafile_vision.go`, `convert_*.go`, `colors.go`, `label.go`. +- Faces/people: `faces_*.go` (audit, clustering, matching, optimize). +- Backups: `backup/` (database and sidecar YAML backup/restore helpers). +- Downloads: `dl/` (export and download handlers/helpers). +- Service registry: `get/` (registry lookups and helper commands). +- Tests & fixtures: `*_test.go`, `testdata/`, uses shared test config (`config.TestConfig()`). + +### Related Packages & Docs + +- [`internal/entity`](../entity) — persistence models and DB helpers used by the indexer. +- [`internal/server`](../server/README.md) — HTTP routing that calls into this package. +- [`internal/meta`](../meta/README.md) — metadata extraction (EXIF/JSON) feeding `MediaFile.MetaData()`. +- [`internal/ffmpeg`](../ffmpeg/README.md) — media transcoding helpers used by converters. +- [`internal/thumb`](../thumb) — thumbnail generation helpers. + +### Usage & Test Guidelines + +- Indexing: use `IndexMain` / `IndexRelated` via `IndexMediaFile` helpers; prefer `IndexOptions` factories. +- Import: run via `ImportWorker` with `ImportOptions`; stacked handling is driven by metadata and document IDs. +- Converters: use `Convert.ToImage` / `Convert.ToVideo` / `Convert.ToJson`; options come from `config.Config`. +- Vision: thumbnails for vision models are selected in `mediafile_vision.go`; ensure models exist in `internal/ai/vision`. +- Tests: targeted runs keep iteration fast, e.g. + - `go test ./internal/photoprism -run TestMediaFile_ -count=1` + - `go test ./internal/photoprism/index_mediafile_test.go -run TestIndexMediaFile` + Full suite: `go test ./internal/photoprism/...` (heavy; migrates fixtures). +- Fixtures live under `storage/testdata`; tests expect initialized config (`config.TestConfig()` / `config.NewMinimalTestConfigWithDb`). + +### Operational Notes + +- Sub-second EXIF timestamps are preserved through metadata parsing and visible in `MediaFile.MetaData()`; database columns remain second-precision. +- File I/O permissions must use `pkg/fs` modes; overwrite requires explicit `force` flags. +- Exec calls to external tools are parameterized by config paths/binaries (`config.Config`). +- Stacking rules honor document IDs, time/place proximity, and configuration (`StackUUID`, `StackMeta`). diff --git a/internal/photoprism/cleanup_options.go b/internal/photoprism/cleanup_options.go index fc4a5f858..443219285 100644 --- a/internal/photoprism/cleanup_options.go +++ b/internal/photoprism/cleanup_options.go @@ -1,6 +1,6 @@ package photoprism -// CleanUpOptions controls cleanup worker behaviour. +// CleanUpOptions controls cleanup worker behavior. type CleanUpOptions struct { Dry bool } diff --git a/internal/photoprism/colors.go b/internal/photoprism/colors.go index 9e81d734e..74afcaa37 100644 --- a/internal/photoprism/colors.go +++ b/internal/photoprism/colors.go @@ -49,15 +49,16 @@ func (m *MediaFile) Colors(thumbPath string) (perception colors.ColorPerception, for y := 0; y < height; y++ { for x := 0; x < width; x++ { r, g, b, a := img.At(x, y).RGBA() - rgb, _ := colorful.MakeColor(color.RGBA{R: uint8(r), G: uint8(g), B: uint8(b), A: uint8(a)}) + rgb, _ := colorful.MakeColor(color.RGBA{ + R: uint8(r >> 8), //nolint:gosec // value is within 0-255 after shift + G: uint8(g >> 8), //nolint:gosec // value is within 0-255 after shift + B: uint8(b >> 8), //nolint:gosec // value is within 0-255 after shift + A: uint8(a >> 8), //nolint:gosec // value is within 0-255 after shift + }) i := colors.Colorful(rgb) perception.Colors = append(perception.Colors, i) - if _, ok := colorCount[i]; ok == true { - colorCount[i] += colors.Weights[i] - } else { - colorCount[i] = colors.Weights[i] - } + colorCount[i] += colors.Weights[i] if colorCount[i] > mainColorCount { mainColorCount = colorCount[i] diff --git a/internal/photoprism/colors_test.go b/internal/photoprism/colors_test.go index 8a2f91c9b..4fe528bd3 100644 --- a/internal/photoprism/colors_test.go +++ b/internal/photoprism/colors_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/photoprism/photoprism/internal/config" "github.com/photoprism/photoprism/internal/thumb" @@ -198,15 +199,22 @@ func TestMediaFile_Colors(t *testing.T) { } }) t.Run("RandomDocx", func(t *testing.T) { - file, fileErr := NewMediaFile(c.ExamplesPath() + "/Random.docx") - p, fileErr := file.Colors(c.ThumbCachePath()) - assert.Error(t, fileErr, "no color information: not a JPEG file") + file, err := NewMediaFile(c.ExamplesPath() + "/Random.docx") + require.NoError(t, err) + + p, colorErr := file.Colors(c.ThumbCachePath()) + assert.Error(t, colorErr, "no color information: not a JPEG file") t.Log(p) }) t.Run("AnimatedEarthThm", func(t *testing.T) { - file, fileErr := NewMediaFile(c.ExamplesPath() + "/animated-earth.thm") - p, fileErr := file.Colors(c.ThumbCachePath()) - assert.Error(t, fileErr, "no color information: not a JPEG file") + file, err := NewMediaFile(c.ExamplesPath() + "/animated-earth.thm") + if err != nil { + assert.Error(t, err) + return + } + + p, colorErr := file.Colors(c.ThumbCachePath()) + assert.Error(t, colorErr, "no color information: not a JPEG file") t.Log(p) }) } diff --git a/internal/photoprism/config.go b/internal/photoprism/config.go index 58a4b56ab..438a0e433 100644 --- a/internal/photoprism/config.go +++ b/internal/photoprism/config.go @@ -6,7 +6,7 @@ import ( var conf *config.Config -// SetConfig initialises package-level access to the shared Config. +// SetConfig initializes package-level access to the shared Config. func SetConfig(c *config.Config) { if c == nil { panic("config is missing") diff --git a/internal/photoprism/convert_fix.go b/internal/photoprism/convert_fix.go index c101accd4..92490ad3d 100644 --- a/internal/photoprism/convert_fix.go +++ b/internal/photoprism/convert_fix.go @@ -25,11 +25,12 @@ func (w *Convert) FixJpeg(f *MediaFile, force bool) (*MediaFile, error) { return nil, fmt.Errorf("convert: ImageMagick must be enabled to re-encode %s", logName) } - if !f.Exists() { + switch { + case !f.Exists(): return nil, fmt.Errorf("convert: %s not found", logName) - } else if f.Empty() { + case f.Empty(): return nil, fmt.Errorf("convert: %s is empty", logName) - } else if !f.IsJpeg() { + case !f.IsJpeg(): return nil, fmt.Errorf("convert: %s is not a jpeg", logName) } @@ -75,6 +76,7 @@ func (w *Convert) FixJpeg(f *MediaFile, force bool) (*MediaFile, error) { quality := fmt.Sprintf("%d", w.conf.JpegQuality()) resize := fmt.Sprintf("%dx%d>", w.conf.JpegSize(), w.conf.JpegSize()) args := []string{f.FileName(), "-flatten", "-resize", resize, "-quality", quality, cacheName} + // #nosec G204 -- command is constructed from validated config and media file paths. cmd := exec.Command(w.conf.ImageMagickBin(), args...) if fs.FileExists(cacheName) { diff --git a/internal/photoprism/convert_image.go b/internal/photoprism/convert_image.go index dc8cb71de..dd5c521bb 100644 --- a/internal/photoprism/convert_image.go +++ b/internal/photoprism/convert_image.go @@ -25,11 +25,12 @@ func (w *Convert) ToImage(f *MediaFile, force bool) (result *MediaFile, err erro return nil, fmt.Errorf("convert: no media file provided for processing - you may have found a bug") } - if !f.Exists() { + switch { + case !f.Exists(): return nil, fmt.Errorf("convert: %s not found", clean.Log(f.RootRelName())) - } else if f.Empty() { + case f.Empty(): return nil, fmt.Errorf("convert: %s is empty", clean.Log(f.RootRelName())) - } else if f.IsThumb() { + case f.IsThumb(): return nil, fmt.Errorf("convert: %s is a thumbnail image", clean.Log(f.RootRelName())) } @@ -50,19 +51,22 @@ func (w *Convert) ToImage(f *MediaFile, force bool) (result *MediaFile, err erro if force && mediaFile.InSidecar() { if removeErr := mediaFile.Remove(); removeErr != nil { return mediaFile, fmt.Errorf("convert: failed removing %s (%s)", clean.Log(mediaFile.RootRelName()), removeErr) - } else { - log.Infof("convert: replacing %s", clean.Log(mediaFile.RootRelName())) } + + log.Infof("convert: replacing %s", clean.Log(mediaFile.RootRelName())) } else { return mediaFile, nil } - } else if f.IsVector() { - if !w.conf.VectorEnabled() { - return nil, fmt.Errorf("convert: vector graphics support disabled (%s)", clean.Log(f.RootRelName())) - } - imageName, _ = fs.FileName(f.FileName(), w.conf.SidecarPath(), w.conf.OriginalsPath(), fs.ExtPng) } else { - imageName, _ = fs.FileName(f.FileName(), w.conf.SidecarPath(), w.conf.OriginalsPath(), fs.ExtJpeg) + switch { + case f.IsVector(): + if !w.conf.VectorEnabled() { + return nil, fmt.Errorf("convert: vector graphics support disabled (%s)", clean.Log(f.RootRelName())) + } + imageName, _ = fs.FileName(f.FileName(), w.conf.SidecarPath(), w.conf.OriginalsPath(), fs.ExtPng) + default: + imageName, _ = fs.FileName(f.FileName(), w.conf.SidecarPath(), w.conf.OriginalsPath(), fs.ExtJpeg) + } } if !w.conf.SidecarWritable() { @@ -195,8 +199,7 @@ func (w *Convert) ToImage(f *MediaFile, force bool) (result *MediaFile, err erro } // Change the Exif orientation of the generated file if required. - switch fileOrientation { - case media.ResetOrientation: + if fileOrientation == media.ResetOrientation { if err = result.ChangeOrientation(1); err != nil { log.Warnf("convert: %s in %s (change orientation)", err, clean.Log(result.RootRelName())) } diff --git a/internal/photoprism/convert_image_jpeg.go b/internal/photoprism/convert_image_jpeg.go index 9c9b4e8db..84684275f 100644 --- a/internal/photoprism/convert_image_jpeg.go +++ b/internal/photoprism/convert_image_jpeg.go @@ -26,6 +26,7 @@ func (w *Convert) JpegConvertCmds(f *MediaFile, jpegName string, xmpName string) // see https://ss64.com/osx/sips.html. if (f.IsRaw() || f.IsHeif()) && w.conf.SipsEnabled() && w.sipsExclude.Allow(fileExt) { result = append(result, NewConvertCmd( + // #nosec G204 -- arguments are built from validated config and file paths. exec.Command(w.conf.SipsBin(), "-Z", maxSize, "-s", "format", "jpeg", "--out", jpegName, f.FileName())), ) } @@ -40,6 +41,7 @@ func (w *Convert) JpegConvertCmds(f *MediaFile, jpegName string, xmpName string) // Use "heif-dec" or "heif-convert" to convert HEIC/HEIF and AVIF image files to JPEG. if (f.IsHeic() || f.IsAvif()) && w.conf.HeifConvertEnabled() { result = append(result, NewConvertCmd( + // #nosec G204 -- arguments are built from validated config and file paths. exec.Command(w.conf.HeifConvertBin(), "-q", w.conf.JpegQuality().String(), f.FileName(), jpegName)). WithOrientation(w.conf.HeifConvertOrientation()), ) @@ -78,6 +80,7 @@ func (w *Convert) JpegConvertCmds(f *MediaFile, jpegName string, xmpName string) } result = append(result, NewConvertCmd( + // #nosec G204 -- arguments are built from validated config and file paths. exec.Command(w.conf.DarktableBin(), args...)), ) } @@ -89,6 +92,7 @@ func (w *Convert) JpegConvertCmds(f *MediaFile, jpegName string, xmpName string) args := []string{"-o", jpegName, "-p", profile, "-s", "-d", jpegQuality, "-js3", "-b8", "-c", f.FileName()} result = append(result, NewConvertCmd( + // #nosec G204 -- arguments are built from validated config and file paths. exec.Command(w.conf.RawTherapeeBin(), args...)), ) } @@ -98,6 +102,7 @@ func (w *Convert) JpegConvertCmds(f *MediaFile, jpegName string, xmpName string) if f.IsDng() && w.conf.ExifToolEnabled() { // Example: exiftool -b -PreviewImage -w IMG_4691.DNG.jpg IMG_4691.DNG result = append(result, NewConvertCmd( + // #nosec G204 -- arguments are built from validated config and file paths. exec.Command(w.conf.ExifToolBin(), "-q", "-q", "-b", "-PreviewImage", f.FileName())), ) } @@ -105,6 +110,7 @@ func (w *Convert) JpegConvertCmds(f *MediaFile, jpegName string, xmpName string) // Use "djxl" to convert JPEG XL images if installed and enabled. if f.IsJpegXL() && w.conf.JpegXLEnabled() { result = append(result, NewConvertCmd( + // #nosec G204 -- arguments are built from validated config and file paths. exec.Command(w.conf.JpegXLDecoderBin(), f.FileName(), jpegName)), ) } @@ -114,19 +120,23 @@ func (w *Convert) JpegConvertCmds(f *MediaFile, jpegName string, xmpName string) resize := fmt.Sprintf("%dx%d>", w.conf.JpegSize(), w.conf.JpegSize()) quality := fmt.Sprintf("%d", w.conf.JpegQuality()) - if f.IsImage() && !f.IsJpegXL() && !f.IsRaw() && !f.IsHeif() { + switch { + case f.IsImage() && !f.IsJpegXL() && !f.IsRaw() && !f.IsHeif(): args := []string{f.FileName() + "[0]", "-background", "white", "-alpha", "remove", "-alpha", "off", "-resize", resize, "-quality", quality, jpegName} result = append(result, NewConvertCmd( + // #nosec G204 -- arguments are built from validated config and file paths. exec.Command(w.conf.ImageMagickBin(), args...)), ) - } else if f.IsVector() && w.conf.VectorEnabled() { + case f.IsVector() && w.conf.VectorEnabled(): args := []string{f.FileName() + "[0]", "-background", "black", "-alpha", "remove", "-alpha", "off", "-resize", resize, "-quality", quality, jpegName} result = append(result, NewConvertCmd( + // #nosec G204 -- arguments are built from validated config and file paths. exec.Command(w.conf.ImageMagickBin(), args...)), ) - } else if f.IsDocument() { + case f.IsDocument(): args := []string{"-colorspace", "sRGB", "-density", "300", f.FileName() + "[0]", "-background", "white", "-alpha", "remove", "-alpha", "off", "-resize", resize, "-quality", quality, jpegName} result = append(result, NewConvertCmd( + // #nosec G204 -- arguments are built from validated config and file paths. exec.Command(w.conf.ImageMagickBin(), args...)), ) } diff --git a/internal/photoprism/convert_image_png.go b/internal/photoprism/convert_image_png.go index 5ee0d2a95..d8c5512b8 100644 --- a/internal/photoprism/convert_image_png.go +++ b/internal/photoprism/convert_image_png.go @@ -25,6 +25,7 @@ func (w *Convert) PngConvertCmds(f *MediaFile, pngName string) (result ConvertCm // see https://ss64.com/osx/sips.html. if (f.IsRaw() || f.IsHeif()) && w.conf.SipsEnabled() && w.sipsExclude.Allow(fileExt) { result = append(result, NewConvertCmd( + // #nosec G204 -- arguments are built from validated config and file paths. exec.Command(w.conf.SipsBin(), "-Z", maxSize, "-s", "format", "png", "--out", pngName, f.FileName())), ) } @@ -40,6 +41,7 @@ func (w *Convert) PngConvertCmds(f *MediaFile, pngName string) (result ConvertCm // Use "heif-dec" or "heif-convert" to convert HEIC/HEIF and AVIF image files to PNG. if (f.IsHeic() || f.IsAvif()) && w.conf.HeifConvertEnabled() { result = append(result, NewConvertCmd( + // #nosec G204 -- arguments are built from validated config and file paths. exec.Command(w.conf.HeifConvertBin(), f.FileName(), pngName)). WithOrientation(w.conf.HeifConvertOrientation()), ) @@ -48,6 +50,7 @@ func (w *Convert) PngConvertCmds(f *MediaFile, pngName string) (result ConvertCm // Use "djxl" to convert JPEG XL images if installed and enabled. if f.IsJpegXL() && w.conf.JpegXLEnabled() { result = append(result, NewConvertCmd( + // #nosec G204 -- arguments are built from validated config and file paths. exec.Command(w.conf.JpegXLDecoderBin(), f.FileName(), pngName)), ) } @@ -56,6 +59,7 @@ func (w *Convert) PngConvertCmds(f *MediaFile, pngName string) (result ConvertCm if w.conf.RsvgConvertEnabled() && f.IsSVG() { args := []string{"-a", "-f", "png", "-o", pngName, f.FileName()} result = append(result, NewConvertCmd( + // #nosec G204 -- arguments are built from validated config and file paths. exec.Command(w.conf.RsvgConvertBin(), args...)), ) } @@ -63,19 +67,23 @@ func (w *Convert) PngConvertCmds(f *MediaFile, pngName string) (result ConvertCm // Use ImageMagick for other media file formats if the type and extension are allowed. if w.conf.ImageMagickEnabled() && w.imageMagickExclude.Allow(fileExt) { resize := fmt.Sprintf("%dx%d>", w.conf.PngSize(), w.conf.PngSize()) - if f.IsImage() && !f.IsJpegXL() && !f.IsRaw() && !f.IsHeif() { + switch { + case f.IsImage() && !f.IsJpegXL() && !f.IsRaw() && !f.IsHeif(): args := []string{f.FileName(), "-flatten", "-resize", resize, pngName} result = append(result, NewConvertCmd( + // #nosec G204 -- arguments are built from validated config and file paths. exec.Command(w.conf.ImageMagickBin(), args...)), ) - } else if f.IsVector() && w.conf.VectorEnabled() { + case f.IsVector() && w.conf.VectorEnabled(): args := []string{f.FileName() + "[0]", "-resize", resize, pngName} result = append(result, NewConvertCmd( + // #nosec G204 -- arguments are built from validated config and file paths. exec.Command(w.conf.ImageMagickBin(), args...)), ) - } else if f.IsDocument() { + case f.IsDocument(): args := []string{"-colorspace", "sRGB", "-density", "300", f.FileName() + "[0]", "-background", "white", "-alpha", "remove", "-alpha", "off", "-resize", resize, pngName} result = append(result, NewConvertCmd( + // #nosec G204 -- arguments are built from validated config and file paths. exec.Command(w.conf.ImageMagickBin(), args...)), ) } diff --git a/internal/photoprism/convert_sidecar_json.go b/internal/photoprism/convert_sidecar_json.go index 08f298bd2..f4fafe932 100644 --- a/internal/photoprism/convert_sidecar_json.go +++ b/internal/photoprism/convert_sidecar_json.go @@ -42,6 +42,7 @@ func (w *Convert) ToJson(f *MediaFile, force bool) (jsonName string, err error) } // Create ExifTool command with arguments. + // #nosec G204 -- arguments are built from validated config and file paths. cmd := exec.Command(w.conf.ExifToolBin(), args...) // Command environment, output and errors. @@ -66,13 +67,13 @@ func (w *Convert) ToJson(f *MediaFile, force bool) (jsonName string, err error) } // Write output to file (make parent dir robustly in case a parallel test cleaned the cache). - if err = os.WriteFile(jsonName, []byte(out.String()), fs.ModeFile); err != nil { + if err = os.WriteFile(jsonName, out.Bytes(), fs.ModeFile); err != nil { // If the parent directory vanished due to concurrent cleanup, recreate and retry once. if !os.IsNotExist(err) { return "", err } else if err = fs.MkdirAll(filepath.Dir(jsonName)); err != nil { return "", err - } else if err = os.WriteFile(jsonName, []byte(out.String()), fs.ModeFile); err != nil { + } else if err = os.WriteFile(jsonName, out.Bytes(), fs.ModeFile); err != nil { return "", err } } diff --git a/internal/photoprism/convert_video_avc.go b/internal/photoprism/convert_video_avc.go index da43f0a60..dd5bc54b3 100644 --- a/internal/photoprism/convert_video_avc.go +++ b/internal/photoprism/convert_video_avc.go @@ -187,6 +187,7 @@ func (w *Convert) TranscodeToAvcCmd(f *MediaFile, avcName string, encoder encode // Try to transcode animated WebP images with ImageMagick. if w.conf.ImageMagickEnabled() && f.IsWebp() && w.imageMagickExclude.Allow(fileExt) { + // #nosec G204 -- arguments are built from validated config and file paths. return exec.Command(w.conf.ImageMagickBin(), f.FileName(), avcName), false, nil } diff --git a/internal/photoprism/faces_audit.go b/internal/photoprism/faces_audit.go index ba86ea439..09d403726 100644 --- a/internal/photoprism/faces_audit.go +++ b/internal/photoprism/faces_audit.go @@ -1,7 +1,7 @@ package photoprism import ( - "crypto/sha1" + "crypto/sha1" //nolint:gosec // SHA1 is kept for backward-compatible audit hashing. "encoding/base32" "fmt" "math" @@ -111,14 +111,16 @@ func (w *Faces) Audit(fix bool, subjUID string) (err error) { if len(stubborn) > 0 { counts, countErr := query.MarkerCountsByFaceIDs(stubbornIDs) - if countErr != nil { + + switch { + case countErr != nil: logErr("faces", "marker counts", countErr) - } else if subjUID != "" { + case subjUID != "": log.Warnf("faces: %s awaiting merge for subject %s", english.Plural(len(stubborn), "manual cluster", "manual clusters"), entity.SubjNames.Log(subjUID)) for _, entry := range stubborn { log.Warnf("faces: cluster %s retry=%d markers=%d notes=%s", entry.ID, entry.MergeRetry, counts[entry.ID], clean.Log(entry.MergeNotes)) } - } else { + default: log.Warnf("faces: %s pending manual cluster merge – use 'photoprism faces audit --subject=' for details", english.Plural(len(stubborn), "manual cluster", "manual clusters")) } } @@ -204,11 +206,12 @@ func (w *Faces) Audit(fix bool, subjUID string) (err error) { } // Show conflict resolution results. - if conflicts == 0 { + switch { + case conflicts == 0: log.Infof("faces: found no ambiguous subjects") - } else if !fix { + case !fix: log.Infof("faces: found %s", english.Plural(conflicts, "ambiguous subject", "ambiguous subjects")) - } else { + default: log.Infof("faces: found %s, %d resolved", english.Plural(conflicts, "ambiguous subject", "ambiguous subjects"), resolved) } @@ -262,7 +265,8 @@ func (w *Faces) Audit(fix bool, subjUID string) (err error) { continue } - if m.SubjUID != faceEntry.SubjUID { + switch { + case m.SubjUID != faceEntry.SubjUID: dist := -1.0 if emb := m.Embeddings(); !emb.Empty() { dist = minEmbeddingDistance(faceEntry.Embedding(), emb) @@ -272,12 +276,11 @@ func (w *Faces) Audit(fix bool, subjUID string) (err error) { m.MarkerUID, entity.SrcString(m.SubjSrc), markerSubject, m.SubjUID, m.FaceID, entity.SrcString(faceEntry.FaceSrc), faceSubject, faceEntry.SubjUID) - if !fix { + switch { + case !fix: log.Warnf("%s", msg) continue - } - - if m.SubjSrc == entity.SrcManual { + case m.SubjSrc == entity.SrcManual: updates := entity.Values{"face_id": "", "face_dist": -1.0, "matched_at": nil, "marker_review": true} if err := entity.Db().Model(&entity.Marker{}). @@ -288,28 +291,28 @@ func (w *Faces) Audit(fix bool, subjUID string) (err error) { log.Warnf("%s – kept manual subject and cleared conflicting face id", msg) } continue - } + default: + updates := entity.Values{ + "subj_uid": faceEntry.SubjUID, + "subj_src": entity.SrcAuto, + "marker_review": false, + } - updates := entity.Values{ - "subj_uid": faceEntry.SubjUID, - "subj_src": entity.SrcAuto, - "marker_review": false, - } + if dist >= 0 { + updates["face_dist"] = dist + } - if dist >= 0 { - updates["face_dist"] = dist + if err := entity.Db().Model(&entity.Marker{}). + Where("marker_uid = ?", m.MarkerUID). + UpdateColumns(updates).Error; err != nil { + log.Errorf("faces: failed aligning marker %s with face %s (%s)", m.MarkerUID, m.FaceID, err) + } else { + log.Infof("faces: updated marker %s to match face %s subject %s (%s)", m.MarkerUID, m.FaceID, faceSubject, faceEntry.SubjUID) + } } - - if err := entity.Db().Model(&entity.Marker{}). - Where("marker_uid = ?", m.MarkerUID). - UpdateColumns(updates).Error; err != nil { - log.Errorf("faces: failed aligning marker %s with face %s (%s)", m.MarkerUID, m.FaceID, err) - } else { - log.Infof("faces: updated marker %s to match face %s subject %s (%s)", m.MarkerUID, m.FaceID, faceSubject, faceEntry.SubjUID) - } - } else if m.MarkerName != "" { + case m.MarkerName != "": log.Infof("faces: marker %s with %s subject name %s conflicts with face %s (%s) of subject %s (%s)", m.MarkerUID, entity.SrcString(m.SubjSrc), clean.Log(m.MarkerName), m.FaceID, entity.SrcString(faceEntry.FaceSrc), faceSubject, faceEntry.SubjUID) - } else { + default: log.Infof("faces: marker %s with unknown subject (%s) conflicts with face %s (%s) of subject %s (%s)", m.MarkerUID, entity.SrcString(m.SubjSrc), m.FaceID, entity.SrcString(faceEntry.FaceSrc), faceSubject, faceEntry.SubjUID) } @@ -434,6 +437,7 @@ func (w *Faces) normalizeStoredEmbeddings(fix bool) (normalized, rekeyed, distan continue } + //nolint:gosec // SHA1 is sufficient for non-security audit hashing. sumBytes := sha1.Sum(normalizedJSON) newID := base32.StdEncoding.EncodeToString(sumBytes[:]) diff --git a/internal/photoprism/faces_audit_test.go b/internal/photoprism/faces_audit_test.go index 1b7b9a685..7e1e25439 100644 --- a/internal/photoprism/faces_audit_test.go +++ b/internal/photoprism/faces_audit_test.go @@ -1,7 +1,7 @@ package photoprism import ( - "crypto/sha1" + "crypto/sha1" //nolint:gosec // SHA1 retained for legacy audit IDs. "encoding/base32" "math" "testing" @@ -58,6 +58,7 @@ func TestFaces_AuditNormalizesEmbeddings(t *testing.T) { rawJSON := raw.JSON() + //nolint:gosec // legacy ID compatibility relies on SHA1. original := sha1.Sum(rawJSON) oldID := base32.StdEncoding.EncodeToString(original[:]) @@ -87,6 +88,7 @@ func TestFaces_AuditNormalizesEmbeddings(t *testing.T) { require.NoError(t, entity.Db().Create(marker).Error) + //nolint:gosec // legacy ID compatibility relies on SHA1. hashNorm := sha1.Sum(normalizeEmbeddingCopy(raw).JSON()) expectedID := base32.StdEncoding.EncodeToString(hashNorm[:]) diff --git a/internal/photoprism/faces_cluster_bench_test.go b/internal/photoprism/faces_cluster_bench_test.go index 7c02409a0..24ec5516b 100644 --- a/internal/photoprism/faces_cluster_bench_test.go +++ b/internal/photoprism/faces_cluster_bench_test.go @@ -6,7 +6,7 @@ import ( "github.com/photoprism/photoprism/internal/ai/face" ) -// BenchmarkClusterMaterialize compares pre-sized versus legacy cluster materialisation. +// BenchmarkClusterMaterialize compares pre-sized versus legacy cluster materialization. func BenchmarkClusterMaterialize(b *testing.B) { const ( clusterCount = 64 diff --git a/internal/photoprism/faces_match.go b/internal/photoprism/faces_match.go index 40a6841a4..747e6a89a 100644 --- a/internal/photoprism/faces_match.go +++ b/internal/photoprism/faces_match.go @@ -418,8 +418,9 @@ func embeddingSignHash(values []float64) uint32 { } for i := 0; i < limit; i++ { - if values[i] >= 0 { - hash |= 1 << uint(i) + if values[i] >= 0 && i < 32 { + //nolint:gosec // shift count bounded by 32 bits. + hash |= 1 << uint32(i) } } diff --git a/internal/photoprism/faces_match_bench_test.go b/internal/photoprism/faces_match_bench_test.go index d77022d99..d2336fd9e 100644 --- a/internal/photoprism/faces_match_bench_test.go +++ b/internal/photoprism/faces_match_bench_test.go @@ -35,7 +35,7 @@ func BenchmarkSelectBestFace(b *testing.B) { } } -// BenchmarkSelectBestFaceLegacy captures the legacy behaviour that scans every face. +// BenchmarkSelectBestFaceLegacy captures the legacy behavior that scans every face. func BenchmarkSelectBestFaceLegacy(b *testing.B) { const candidateCount = 1024 diff --git a/internal/photoprism/faces_optimize.go b/internal/photoprism/faces_optimize.go index 65da620c9..36a0da679 100644 --- a/internal/photoprism/faces_optimize.go +++ b/internal/photoprism/faces_optimize.go @@ -22,7 +22,7 @@ func (w *Faces) Optimize() (result FacesOptimizeResult, err error) { } // OptimizeFor optimizes the face lookup table for the given subject UID (or all when empty). -func (w *Faces) OptimizeFor(subj_uid string) (result FacesOptimizeResult, err error) { +func (w *Faces) OptimizeFor(subjUID string) (result FacesOptimizeResult, err error) { if w.Disabled() { return result, fmt.Errorf("face recognition is disabled") } @@ -35,14 +35,14 @@ func (w *Faces) OptimizeFor(subj_uid string) (result FacesOptimizeResult, err er var faces entity.Faces // Fetch manually added faces from the database. - if faces, err = query.ManuallyAddedFaces(false, false, subj_uid); err != nil { + if faces, err = query.ManuallyAddedFaces(false, false, subjUID); err != nil { return result, err } else if n = len(faces) - 1; n < 1 { // Need at least 2 faces to optimize. break } - log.Debugf("faces: optimize for %s itr %d n %d", subj_uid, i, n) + log.Debugf("faces: optimize for %s itr %d n %d", subjUID, i, n) // Find and merge matching faces. for j := 0; j <= n; j++ { diff --git a/internal/photoprism/import_worker.go b/internal/photoprism/import_worker.go index ba9eaa4c7..169a848df 100644 --- a/internal/photoprism/import_worker.go +++ b/internal/photoprism/import_worker.go @@ -168,7 +168,7 @@ func ImportWorker(jobs <-chan ImportJob) { // Ensure that a JPEG and the configured default thumbnail sizes exist. if img, imgErr := f.PreviewImage(); imgErr != nil { log.Error(imgErr) - } else if limitErr, _ := img.ExceedsResolution(o.ResolutionLimit); limitErr != nil { + } else if _, limitErr := img.ExceedsResolution(o.ResolutionLimit); limitErr != nil { log.Errorf("import: %s", limitErr) continue } else if thumbsErr := img.GenerateThumbnails(imp.thumbPath(), false); thumbsErr != nil { @@ -193,10 +193,10 @@ func ImportWorker(jobs <-chan ImportJob) { main := related.Main // Enforce file size and resolution limits. - if limitErr, _ := main.ExceedsBytes(o.ByteLimit); limitErr != nil { + if _, limitErr := main.ExceedsBytes(o.ByteLimit); limitErr != nil { log.Warnf("import: %s", limitErr) continue - } else if limitErr, _ = main.ExceedsResolution(o.ResolutionLimit); limitErr != nil { + } else if _, limitErr = main.ExceedsResolution(o.ResolutionLimit); limitErr != nil { log.Warnf("import: %s", limitErr) continue } @@ -235,9 +235,9 @@ func ImportWorker(jobs <-chan ImportJob) { done[file.FileName()] = true // Show warning if sidecar file exceeds size or resolution limit. - if limitErr, _ := file.ExceedsBytes(o.ByteLimit); limitErr != nil { + if _, limitErr := file.ExceedsBytes(o.ByteLimit); limitErr != nil { log.Warnf("import: %s", limitErr) - } else if limitErr, _ = file.ExceedsResolution(o.ResolutionLimit); limitErr != nil { + } else if _, limitErr = file.ExceedsResolution(o.ResolutionLimit); limitErr != nil { log.Warnf("import: %s", limitErr) } diff --git a/internal/photoprism/index.go b/internal/photoprism/index.go index c4a147605..177114681 100644 --- a/internal/photoprism/index.go +++ b/internal/photoprism/index.go @@ -11,8 +11,6 @@ import ( "github.com/karrick/godirwalk" - "github.com/photoprism/photoprism/internal/ai/classify" - "github.com/photoprism/photoprism/internal/ai/vision" "github.com/photoprism/photoprism/internal/config" "github.com/photoprism/photoprism/internal/entity" "github.com/photoprism/photoprism/internal/event" @@ -51,22 +49,6 @@ func NewIndex(conf *config.Config, convert *Convert, files *Files, photos *Photo return i } -func (ind *Index) shouldFlagPrivate(labels classify.Labels) bool { - if ind == nil || ind.conf == nil || !ind.conf.DetectNSFW() { - return false - } - - threshold := vision.Config.Thresholds.GetNSFW() - - for _, label := range labels { - if label.NSFW || label.NSFWConfidence >= threshold { - return true - } - } - - return false -} - func (ind *Index) originalsPath() string { return ind.conf.OriginalsPath() } @@ -262,20 +244,25 @@ func (ind *Index) Start(o IndexOptions) (found fs.Done, updated int) { if found[f.FileName()].Processed() { // Ignore already processed files. continue - } else if limitErr, fileSize := f.ExceedsBytes(o.ByteLimit); fileSize == 0 || ind.files.Indexed(f.RootRelName(), f.Root(), f.ModTime(), o.Rescan) { - // Flag file as found but not processed. - found[f.FileName()] = fs.Found - continue - } else if limitErr == nil { - // Add to file list. - files = append(files, f) - } else if related.Main.FileName() != f.FileName() { - // Sidecar file is too large, ignore. - log.Infof("index: %s", limitErr) } else { - // Main file is too large, skip all. - log.Warnf("index: %s", limitErr) - skip = true + fileSize, limitErr := f.ExceedsBytes(o.ByteLimit) + + switch { + case fileSize == 0 || ind.files.Indexed(f.RootRelName(), f.Root(), f.ModTime(), o.Rescan): + // Flag file as found but not processed. + found[f.FileName()] = fs.Found + continue + case limitErr == nil: + // Add to file list. + files = append(files, f) + case related.Main.FileName() != f.FileName(): + // Sidecar file is too large, ignore. + log.Infof("index: %s", limitErr) + default: + // Main file is too large, skip all. + log.Warnf("index: %s", limitErr) + skip = true + } } found[f.FileName()] = fs.Processed diff --git a/internal/photoprism/index_faces.go b/internal/photoprism/index_faces.go index 7cd1a1baa..1f892563d 100644 --- a/internal/photoprism/index_faces.go +++ b/internal/photoprism/index_faces.go @@ -78,7 +78,9 @@ func ApplyDetectedFaces(file *entity.File, faces face.Faces) (saved bool, count return false, 0, nil } - count, err = file.UpdatePhotoFaceCount() + if count, err = file.UpdatePhotoFaceCount(); err != nil { + return true, count, err + } return true, count, nil } diff --git a/internal/photoprism/index_main.go b/internal/photoprism/index_main.go index b3687aac0..f0d07fc23 100644 --- a/internal/photoprism/index_main.go +++ b/internal/photoprism/index_main.go @@ -25,11 +25,11 @@ func IndexMain(related *RelatedFiles, ind *Index, o IndexOptions) (result IndexR result.Err = fmt.Errorf("index: skipped %s because it %w", clean.Log(f.RootRelName()), typeErr) result.Status = IndexFailed return result - } else if limitErr, _ := f.ExceedsBytes(o.ByteLimit); limitErr != nil { + } else if _, limitErr := f.ExceedsBytes(o.ByteLimit); limitErr != nil { result.Err = fmt.Errorf("index: %s", limitErr) result.Status = IndexFailed return result - } else if limitErr, _ = f.ExceedsResolution(o.ResolutionLimit); limitErr != nil { + } else if _, limitErr = f.ExceedsResolution(o.ResolutionLimit); limitErr != nil { result.Err = fmt.Errorf("index: %s", limitErr) result.Status = IndexFailed return result @@ -49,7 +49,7 @@ func IndexMain(related *RelatedFiles, ind *Index, o IndexOptions) (result IndexR return result } else if img == nil { log.Debugf("index: skipped creating preview image for %s", clean.Log(f.RootRelName())) - } else if limitErr, _ := img.ExceedsResolution(o.ResolutionLimit); limitErr != nil { + } else if _, limitErr := img.ExceedsResolution(o.ResolutionLimit); limitErr != nil { result.Err = fmt.Errorf("index: %s", limitErr) result.Status = IndexFailed return result diff --git a/internal/photoprism/index_mediafile.go b/internal/photoprism/index_mediafile.go index baf3e4276..a703f44da 100644 --- a/internal/photoprism/index_mediafile.go +++ b/internal/photoprism/index_mediafile.go @@ -136,7 +136,8 @@ func (ind *Index) UserMediaFile(m *MediaFile, o IndexOptions, originalName, phot } // Find existing photo if a photo uid was provided or file has not been indexed yet... - if !fileExists && photoUID != "" { + switch { + case !fileExists && photoUID != "": // Find existing photo by UID. photoQuery = entity.UnscopedDb().First(&photo, "photo_uid = ?", photoUID) @@ -149,7 +150,7 @@ func (ind *Index) UserMediaFile(m *MediaFile, o IndexOptions, originalName, phot result.Err = fmt.Errorf("index: failed indexing %s, unknown photo uid %s (%s)", logName, photoUID, photoQuery.Error) return result } - } else if !fileExists { + case !fileExists: // Find existing photo by matching path and name. if photoQuery = entity.UnscopedDb().First(&photo, "photo_path = ? AND photo_name = ?", filePath, fullBase); photoQuery.Error == nil || fileBase == fullBase || !o.Stack { // Skip next query. @@ -184,19 +185,20 @@ func (ind *Index) UserMediaFile(m *MediaFile, o IndexOptions, originalName, phot } } } - } else if fileExists { + case fileExists: // Find photo by the id or uid assigned to the file. - if file.PhotoID > 0 { + switch { + case file.PhotoID > 0: photoQuery = entity.UnscopedDb().First(&photo, "id = ?", file.PhotoID) - } else if rnd.IsUID(file.PhotoUID, entity.PhotoUID) { + case rnd.IsUID(file.PhotoUID, entity.PhotoUID): photoQuery = entity.UnscopedDb().First(&photo, "photo_uid = ?", file.PhotoUID) - } else { + default: // Should never happen. result.Status = IndexFailed result.Err = fmt.Errorf("index: file %s has no photo id or uid assigned - you may have found a bug, please report", logName) return result } - } else { + default: // Should never happen. result.Status = IndexFailed result.Err = fmt.Errorf("index: unexpectedly failed indexing %s - you may have found a bug, please report", logName) @@ -215,13 +217,14 @@ func (ind *Index) UserMediaFile(m *MediaFile, o IndexOptions, originalName, phot } // Detect and report file changes. - if fileRenamed { + switch { + case fileRenamed: fileChanged = true log.Debugf("index: %s was renamed", clean.Log(m.BaseName())) - } else if file.Changed(fileSize, modTime) { + case file.Changed(fileSize, modTime): fileChanged = true log.Debugf("index: %s was modified (new size %d, old size %d, new timestamp %d, old timestamp %d)", clean.Log(m.BaseName()), fileSize, file.FileSize, modTime.Unix(), file.ModTime) - } else if file.Missing() { + case file.Missing(): fileChanged = true log.Debugf("index: %s was missing", clean.Log(m.BaseName())) } @@ -583,13 +586,14 @@ func (ind *Index) UserMediaFile(m *MediaFile, o IndexOptions, originalName, phot // If the media type is still set to "image" and has not been // manually modified, then check and update it as needed. if photo.HasMediaType(media.Image) { - if m.IsAnimatedImage() { + switch { + case m.IsAnimatedImage(): photo.SetMediaType(media.Animated, entity.SrcAuto) - } else if m.IsRaw() { + case m.IsRaw(): photo.SetMediaType(media.Raw, entity.SrcAuto) - } else if m.IsLive(photo.PhotoDuration) { + case m.IsLive(photo.PhotoDuration): photo.SetMediaType(media.Live, entity.SrcAuto) - } else if m.IsVector() { + case m.IsVector(): photo.SetMediaType(media.Vector, entity.SrcAuto) } } @@ -973,11 +977,12 @@ func (ind *Index) UserMediaFile(m *MediaFile, o IndexOptions, originalName, phot w = append(w, txt.FilenameKeywords(fileBase)...) } - if photo.OriginalName == "" { + switch { + case photo.OriginalName == "": // Do nothing. - } else if fs.IsGenerated(photo.OriginalName) { + case fs.IsGenerated(photo.OriginalName): w = append(w, txt.FilenameKeywords(filepath.Dir(photo.OriginalName))...) - } else { + default: w = append(w, txt.FilenameKeywords(photo.OriginalName)...) } @@ -1072,14 +1077,17 @@ func (ind *Index) UserMediaFile(m *MediaFile, o IndexOptions, originalName, phot // Do nothing. } else if original, merged, err := photo.Merge(Config().Settings().StackMeta(), Config().Settings().StackUUID()); err != nil { log.Errorf("index: %s in %s (merge)", err.Error(), logName) - } else if len(merged) == 1 && original.ID == photo.ID { - log.Infof("index: merged one existing photo with %s", logName) - } else if len(merged) > 1 && original.ID == photo.ID { - log.Infof("index: merged %d existing photos with %s", len(merged), logName) - } else if len(merged) > 0 && original.ID != photo.ID { - log.Infof("index: merged %s with existing photo id %d", logName, original.ID) - result.Status = IndexStacked - return result + } else { + switch { + case len(merged) == 1 && original.ID == photo.ID: + log.Infof("index: merged one existing photo with %s", logName) + case len(merged) > 1 && original.ID == photo.ID: + log.Infof("index: merged %d existing photos with %s", len(merged), logName) + case len(merged) > 0 && original.ID != photo.ID: + log.Infof("index: merged %s with existing photo id %d", logName, original.ID) + result.Status = IndexStacked + return result + } } // Create backup of picture metadata in sidecar YAML file. diff --git a/internal/photoprism/index_mediafile_test.go b/internal/photoprism/index_mediafile_test.go index 694f93020..023366aa0 100644 --- a/internal/photoprism/index_mediafile_test.go +++ b/internal/photoprism/index_mediafile_test.go @@ -38,8 +38,9 @@ func TestIndex_MediaFile(t *testing.T) { t.Logf("size in megapixel: %d", mediaFile.Megapixels()) - limitErr, _ := mediaFile.ExceedsResolution(cfg.ResolutionLimit()) - t.Logf("index: %s", limitErr) + if _, limitErr := mediaFile.ExceedsResolution(cfg.ResolutionLimit()); limitErr != nil { + t.Logf("index: %s", limitErr) + } assert.Contains(t, words, "marienkäfer") assert.Contains(t, words, "burst") diff --git a/internal/photoprism/index_related.go b/internal/photoprism/index_related.go index a658e675f..ba15ed3f9 100644 --- a/internal/photoprism/index_related.go +++ b/internal/photoprism/index_related.go @@ -21,12 +21,13 @@ func IndexRelated(related RelatedFiles, ind *Index, o IndexOptions) (result Inde done := make(map[string]bool) result = IndexMain(&related, ind, o) - if result.Failed() { + switch { + case result.Failed(): return result - } else if !result.Success() { + case !result.Success(): // Skip related files if indexing was not completely successful. return result - } else if result.Stacked() && related.Len() > 1 { + case result.Stacked() && related.Len() > 1: // Show info if main file was stacked and has additional related files. log.Infof("index: %s has %s", related.MainLogName(), english.Plural(related.Count(), "related file", "related files")) } @@ -58,9 +59,9 @@ func IndexRelated(related RelatedFiles, ind *Index, o IndexOptions) (result Inde } // Show warning if sidecar file exceeds size or resolution limit. - if limitErr, _ := f.ExceedsBytes(o.ByteLimit); limitErr != nil { + if _, limitErr := f.ExceedsBytes(o.ByteLimit); limitErr != nil { log.Warnf("index: %s", limitErr) - } else if limitErr, _ = f.ExceedsResolution(o.ResolutionLimit); limitErr != nil { + } else if _, limitErr = f.ExceedsResolution(o.ResolutionLimit); limitErr != nil { log.Warnf("index: %s", limitErr) } diff --git a/internal/photoprism/label.go b/internal/photoprism/label.go index 346644ecf..87e11f82e 100644 --- a/internal/photoprism/label.go +++ b/internal/photoprism/label.go @@ -84,18 +84,19 @@ func (l Labels) Title(fallback string) string { // Get best label (at the top) label := l[0] - // Get second best label in case the first has high uncertainty + // Get second-best label in case the first has high uncertainty if len(l) > 1 && l[0].Uncertainty > 60 && l[1].Uncertainty <= 60 { label = l[1] } - if fallback != "" && label.Priority < 0 { + switch { + case fallback != "" && label.Priority < 0: return fallback - } else if fallback != "" && label.Priority == 0 && label.Uncertainty > 50 { + case fallback != "" && label.Priority == 0 && label.Uncertainty > 50: return fallback - } else if label.Priority >= -1 && label.Uncertainty <= 60 { + case label.Priority >= -1 && label.Uncertainty <= 60: return label.Name + default: + return fallback } - - return fallback } diff --git a/internal/photoprism/mediafile.go b/internal/photoprism/mediafile.go index 1d1cde22c..0a47f7444 100644 --- a/internal/photoprism/mediafile.go +++ b/internal/photoprism/mediafile.go @@ -68,7 +68,7 @@ type MediaFile struct { imageConfig *image.Config } -// NewMediaFile resolves fileName (following symlinks) and initialises a MediaFile +// NewMediaFile resolves fileName (following symlinks) and initializes a MediaFile // instance. The returned instance is never nil; callers must check the error to // learn whether the path existed or was readable. func NewMediaFile(fileName string) (*MediaFile, error) { @@ -399,7 +399,7 @@ func (m *MediaFile) RootRelName() string { return m.RelName(m.RootPath()) } -// RelName returns the file name relative to directory, sanitising the result for logging. +// RelName returns the file name relative to directory, sanitizing the result for logging. func (m *MediaFile) RelName(directory string) string { return fs.RelName(m.fileName, directory) } @@ -619,6 +619,7 @@ func (m *MediaFile) openFile() (handle *os.File, err error) { return nil, fmt.Errorf("%s %s", err, clean.Log(m.RootRelName())) } + // #nosec G304 -- fileName is resolved from trusted MediaFile path. handle, err = os.Open(fileName) if err != nil { @@ -681,11 +682,12 @@ func (m *MediaFile) Move(filePath string, force bool) (err error) { // Error if destination exists (and is not empty) without the force flag being used. if fs.Exists(filePath) { - if fs.FileExistsIsEmpty(filePath) { + switch { + case fs.FileExistsIsEmpty(filePath): log.Infof("move: replacing empty destination file %s", logName) - } else if force { + case force: log.Warnf("move: overwriting destination file %s", logName) - } else { + default: return fmt.Errorf("move: destination name %s already exists", logName) } } @@ -755,11 +757,12 @@ func (m *MediaFile) Copy(filePath string, force bool) (err error) { // Error if destination exists (and is not empty) without the force flag being used. if fs.Exists(filePath) { - if fs.FileExistsIsEmpty(filePath) { + switch { + case fs.FileExistsIsEmpty(filePath): log.Infof("copy: replacing empty destination file %s", logName) - } else if force { + case force: log.Warnf("copy: overwriting destination file %s", logName) - } else { + default: return fmt.Errorf("copy: destination name %s already exists", logName) } } @@ -781,6 +784,7 @@ func (m *MediaFile) Copy(filePath string, force bool) (err error) { defer thisFile.Close() // Open the target file path for writing, discarding any trailing bytes. + // #nosec G304 -- destination path is validated and absolute. destFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, fs.ModeFile) if err != nil { @@ -1184,13 +1188,14 @@ func (m *MediaFile) IsImageNative() bool { // IsLive checks if the file is a live photo. func (m *MediaFile) IsLive(videoDuration time.Duration) bool { - if !m.InOriginals() { + switch { + case !m.InOriginals(): // Live Photos must be located in the Originals folder. return false - } else if !m.HasMediaType(media.Video, media.Image, media.Live) { + case !m.HasMediaType(media.Video, media.Image, media.Live): // Live Photos may only consist of video, image, or live files. return false - } else if videoDuration > media.LiveMaxDuration { + case videoDuration > media.LiveMaxDuration: // Live Photos can include a maximum of 3.1 seconds of video. return false } @@ -1409,26 +1414,34 @@ func (m *MediaFile) Megapixels() (resolution int) { } // ExceedsBytes checks if the file exceeds the specified size limit in bytes. -func (m *MediaFile) ExceedsBytes(limit int64) (err error, fileSize int64) { - if fileSize = m.FileSize(); limit <= 0 { - return nil, fileSize - } else if fileSize <= 0 || fileSize <= limit { - return nil, fileSize - } else { - return fmt.Errorf("%s exceeds file size limit (%s / %s)", clean.Log(m.RootRelName()), humanize.Bytes(uint64(fileSize)), humanize.Bytes(uint64(limit))), fileSize +func (m *MediaFile) ExceedsBytes(limit int64) (fileSize int64, err error) { + fileSize = m.FileSize() + + switch { + case limit <= 0: + return fileSize, nil + case fileSize <= 0 || fileSize <= limit: + return fileSize, nil + default: + return fileSize, fmt.Errorf("%s exceeds file size limit (%s / %s)", clean.Log(m.RootRelName()), humanize.Bytes(uint64(fileSize)), humanize.Bytes(uint64(limit))) } } // ExceedsResolution checks if an image in a natively supported format exceeds the configured resolution limit in megapixels. -func (m *MediaFile) ExceedsResolution(limit int) (err error, resolution int) { - if limit <= 0 { - return nil, resolution - } else if !m.IsImage() { - return nil, resolution - } else if resolution = m.Megapixels(); resolution <= 0 || resolution <= limit { - return nil, resolution - } else { - return fmt.Errorf("%s exceeds resolution limit (%d / %d MP)", clean.Log(m.RootRelName()), resolution, limit), resolution +func (m *MediaFile) ExceedsResolution(limit int) (resolution int, err error) { + switch { + case limit <= 0: + return resolution, nil + case !m.IsImage(): + return resolution, nil + default: + resolution = m.Megapixels() + + if resolution <= 0 || resolution <= limit { + return resolution, nil + } + + return resolution, fmt.Errorf("%s exceeds resolution limit (%d / %d MP)", clean.Log(m.RootRelName()), resolution, limit) } } @@ -1568,6 +1581,7 @@ func (m *MediaFile) ColorProfile() string { } // Open file. + // #nosec G304 -- fileName is resolved from trusted MediaFile path. fileReader, err := os.Open(fileName) if err != nil { diff --git a/internal/photoprism/mediafile_fs_test.go b/internal/photoprism/mediafile_fs_test.go index c54106c06..3b9b6574c 100644 --- a/internal/photoprism/mediafile_fs_test.go +++ b/internal/photoprism/mediafile_fs_test.go @@ -24,6 +24,7 @@ func writeFile(t *testing.T, p string, data []byte) { func readFile(t *testing.T, p string) []byte { t.Helper() + // #nosec G304 -- test reads from controlled temp directory. b, err := os.ReadFile(p) if err != nil { t.Fatal(err) diff --git a/internal/photoprism/mediafile_heic_test.go b/internal/photoprism/mediafile_heic_test.go index c310bd004..75b46262a 100644 --- a/internal/photoprism/mediafile_heic_test.go +++ b/internal/photoprism/mediafile_heic_test.go @@ -32,14 +32,12 @@ func TestMediaFile_Heic(t *testing.T) { convert := NewConvert(conf) // Create JPEG image. - jpeg, err := convert.ToImage(img, false) - - if err != nil { + if _, err = convert.ToImage(img, false); err != nil { t.Fatal(err) } // Replace JPEG image. - jpeg, err = convert.ToImage(img, true) + jpeg, err := convert.ToImage(img, true) if err != nil { t.Fatal(err) @@ -97,14 +95,12 @@ func TestMediaFile_Heic(t *testing.T) { convert := NewConvert(c) // Create JPEG image. - jpeg, err := convert.ToImage(img, false) - - if err != nil { + if _, err = convert.ToImage(img, false); err != nil { t.Fatal(err) } // Replace JPEG image. - jpeg, err = convert.ToImage(img, true) + jpeg, err := convert.ToImage(img, true) if err != nil { t.Fatal(err) diff --git a/internal/photoprism/mediafile_meta_test.go b/internal/photoprism/mediafile_meta_test.go index 31eec6521..e6d1ec63e 100644 --- a/internal/photoprism/mediafile_meta_test.go +++ b/internal/photoprism/mediafile_meta_test.go @@ -7,6 +7,7 @@ import ( "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/photoprism/photoprism/internal/config" "github.com/photoprism/photoprism/internal/meta" @@ -120,6 +121,10 @@ func TestMediaFile_CreateExifToolJson(t *testing.T) { jsonName, err := mediaFile.ExifToolJsonName() + if err != nil { + t.Fatal(err) + } + if fs.FileExists(jsonName) { if err = os.Remove(jsonName); err != nil { t.Error(err) @@ -173,10 +178,11 @@ func TestMediaFile_CreateExifToolJson(t *testing.T) { assert.Equal(t, fs.VideoMp4, mediaFile.FileType()) jsonName, err := mediaFile.ExifToolJsonName() + require.NoError(t, err) if fs.FileExists(jsonName) { - if err = os.Remove(jsonName); err != nil { - t.Error(err) + if rmErr := os.Remove(jsonName); rmErr != nil { + t.Error(rmErr) } } @@ -221,9 +227,13 @@ func TestMediaFile_CreateExifToolJson(t *testing.T) { jsonName, err := mediaFile.ExifToolJsonName() + if err != nil { + t.Fatal(err) + } + if fs.FileExists(jsonName) { - if err = os.Remove(jsonName); err != nil { - t.Error(err) + if rmErr := os.Remove(jsonName); rmErr != nil { + t.Error(rmErr) } } diff --git a/internal/photoprism/mediafile_related.go b/internal/photoprism/mediafile_related.go index e5069fd0f..56689b8f0 100644 --- a/internal/photoprism/mediafile_related.go +++ b/internal/photoprism/mediafile_related.go @@ -83,27 +83,26 @@ func (m *MediaFile) RelatedFiles(stripSequence bool) (result RelatedFiles, err e } // Set main file. - if result.Main == nil && f.IsPreviewImage() { + switch { + case result.Main == nil && f.IsPreviewImage(): result.Main = f - } else if f.IsRaw() { + case f.IsRaw(): result.Main = f - } else if f.IsVector() { + case f.IsVector(): result.Main = f - } else if f.IsDocument() { + case f.IsDocument(): result.Main = f - } else if f.IsHeic() { + case f.IsHeic(): isHeic = true result.Main = f - } else if f.IsHeif() { + case f.IsHeif(): result.Main = f - } else if f.IsImage() && !f.IsPreviewImage() && !f.IsThumb() { + case f.IsImage() && !f.IsPreviewImage() && !f.IsThumb(): result.Main = f - } else if f.IsVideo() && !isHeic { + case f.IsVideo() && !isHeic: + result.Main = f + case result.Main != nil && f.IsPreviewImage() && result.Main.IsPreviewImage() && len(result.Main.FileName()) > len(f.FileName()): result.Main = f - } else if result.Main != nil && f.IsPreviewImage() { - if result.Main.IsPreviewImage() && len(result.Main.FileName()) > len(f.FileName()) { - result.Main = f - } } result.Files = append(result.Files, f) diff --git a/internal/photoprism/mediafile_test.go b/internal/photoprism/mediafile_test.go index 2ff7be230..51ab890f0 100644 --- a/internal/photoprism/mediafile_test.go +++ b/internal/photoprism/mediafile_test.go @@ -2031,7 +2031,7 @@ func TestMediaFile_ExceedsBytes(t *testing.T) { if f, err := NewMediaFile("testdata/norway-kjetil-moe.webp"); err != nil { t.Fatal(err) } else { - err, actual := f.ExceedsBytes(3145728) + actual, err := f.ExceedsBytes(3145728) assert.NoError(t, err) assert.Equal(t, int64(30320), actual) assert.True(t, f.Ok()) @@ -2042,7 +2042,7 @@ func TestMediaFile_ExceedsBytes(t *testing.T) { if f, err := NewMediaFile(conf.ExamplesPath() + "/telegram_2020-01-30_09-57-18.jpg"); err != nil { t.Fatal(err) } else { - err, actual := f.ExceedsBytes(-1) + actual, err := f.ExceedsBytes(-1) assert.NoError(t, err) assert.Equal(t, int64(128471), actual) assert.True(t, f.Ok()) @@ -2053,7 +2053,7 @@ func TestMediaFile_ExceedsBytes(t *testing.T) { if f, err := NewMediaFile(conf.ExamplesPath() + "/6720px_white.jpg"); err != nil { t.Fatal(err) } else { - err, actual := f.ExceedsBytes(0) + actual, err := f.ExceedsBytes(0) assert.NoError(t, err) assert.Equal(t, int64(162877), actual) assert.True(t, f.Ok()) @@ -2064,7 +2064,7 @@ func TestMediaFile_ExceedsBytes(t *testing.T) { if f, err := NewMediaFile(conf.ExamplesPath() + "/canon_eos_6d.dng"); err != nil { t.Fatal(err) } else { - err, actual := f.ExceedsBytes(10485760) + actual, err := f.ExceedsBytes(10485760) assert.NoError(t, err) assert.Equal(t, int64(411944), actual) assert.True(t, f.Ok()) @@ -2075,7 +2075,7 @@ func TestMediaFile_ExceedsBytes(t *testing.T) { if f, err := NewMediaFile(conf.ExamplesPath() + "/example.bmp"); err != nil { t.Fatal(err) } else { - err, actual := f.ExceedsBytes(10485760) + actual, err := f.ExceedsBytes(10485760) assert.NoError(t, err) assert.Equal(t, int64(20156), actual) assert.True(t, f.Ok()) @@ -2119,8 +2119,8 @@ func TestMediaFile_ExceedsResolution(t *testing.T) { if f, err := NewMediaFile("testdata/norway-kjetil-moe.webp"); err != nil { t.Fatal(err) } else { - result, actual := f.ExceedsResolution(3) - assert.NoError(t, result) + actual, err := f.ExceedsResolution(3) + assert.NoError(t, err) assert.Equal(t, 0, actual) } }) @@ -2128,8 +2128,8 @@ func TestMediaFile_ExceedsResolution(t *testing.T) { if f, err := NewMediaFile(conf.ExamplesPath() + "/telegram_2020-01-30_09-57-18.jpg"); err != nil { t.Fatal(err) } else { - result, actual := f.ExceedsResolution(3) - assert.NoError(t, result) + actual, err := f.ExceedsResolution(3) + assert.NoError(t, err) assert.Equal(t, 1, actual) } }) @@ -2140,17 +2140,17 @@ func TestMediaFile_ExceedsResolution(t *testing.T) { t.Fatal(err) } - err3, actual3 := f.ExceedsResolution(3) + actual3, err3 := f.ExceedsResolution(3) assert.Error(t, err3) assert.Equal(t, 30, actual3) - err30, actual30 := f.ExceedsResolution(30) + actual30, err30 := f.ExceedsResolution(30) assert.NoError(t, err30) assert.Equal(t, 30, actual30) - err33, actual33 := f.ExceedsResolution(33) + actual33, err33 := f.ExceedsResolution(33) assert.NoError(t, err33) assert.Equal(t, 30, actual33) @@ -2159,8 +2159,8 @@ func TestMediaFile_ExceedsResolution(t *testing.T) { if f, err := NewMediaFile(conf.ExamplesPath() + "/canon_eos_6d.dng"); err != nil { t.Fatal(err) } else { - result, actual := f.ExceedsResolution(3) - assert.NoError(t, result) + actual, err := f.ExceedsResolution(3) + assert.NoError(t, err) assert.Equal(t, 0, actual) } }) @@ -2168,8 +2168,8 @@ func TestMediaFile_ExceedsResolution(t *testing.T) { if f, err := NewMediaFile(conf.ExamplesPath() + "/example.bmp"); err != nil { t.Fatal(err) } else { - result, actual := f.ExceedsResolution(3) - assert.NoError(t, result) + actual, err := f.ExceedsResolution(3) + assert.NoError(t, err) assert.Equal(t, 0, actual) } }) @@ -2576,7 +2576,7 @@ func TestMediaFile_RenameSidecarFiles(t *testing.T) { srcName := filepath.Join(c.SidecarPath(), "foo/bar.jpg.json") dstName := filepath.Join(c.SidecarPath(), "2020/12/foobar.jpg.json") - if err = os.WriteFile(srcName, []byte("{}"), 0666); err != nil { + if err = os.WriteFile(srcName, []byte("{}"), 0o600); err != nil { t.Fatal(err) } @@ -2621,7 +2621,7 @@ func TestMediaFile_RemoveSidecarFiles(t *testing.T) { sidecarName := filepath.Join(c.SidecarPath(), "2020/12/foobar.jpg.json") - if writeErr := os.WriteFile(sidecarName, []byte("{}"), 0666); writeErr != nil { + if writeErr := os.WriteFile(sidecarName, []byte("{}"), 0o600); writeErr != nil { t.Fatal(writeErr) } diff --git a/internal/photoprism/mediafile_thumbs.go b/internal/photoprism/mediafile_thumbs.go index 68ffb4711..903f1f452 100644 --- a/internal/photoprism/mediafile_thumbs.go +++ b/internal/photoprism/mediafile_thumbs.go @@ -149,8 +149,8 @@ func (m *MediaFile) GenerateThumbnails(thumbPath string, force bool) (err error) msg := imgErr.Error() // Non-repairable file error? - if !(strings.Contains(msg, fs.EOF.Error()) || - strings.HasPrefix(msg, "invalid JPEG")) { + if !strings.Contains(msg, fs.EOF.Error()) && + !strings.HasPrefix(msg, "invalid JPEG") { log.Debugf("media: %s in %s", msg, clean.Log(m.RootRelName())) return imgErr } @@ -210,6 +210,7 @@ func (m *MediaFile) ChangeOrientation(val int) (err error) { } cnf := Config() + // #nosec G204 -- arguments are built from validated config and file paths. cmd := exec.Command(cnf.ExifToolBin(), "-overwrite_original", "-n", "-Orientation="+strconv.Itoa(val), m.FileName()) // Fetch command output. diff --git a/internal/photoprism/mediafile_vision.go b/internal/photoprism/mediafile_vision.go index 7fc8acdd7..1ca330f14 100644 --- a/internal/photoprism/mediafile_vision.go +++ b/internal/photoprism/mediafile_vision.go @@ -84,14 +84,15 @@ func (m *MediaFile) GenerateLabels(labelSrc entity.Src) (labels classify.Labels) size := vision.Thumb(vision.ModelTypeLabels) // The thumbnail size may need to be adjusted to use other models. - if size.Name != "" && size.Name != thumb.Tile224 { + switch { + case size.Name != "" && size.Name != thumb.Tile224: sizes = []thumb.Name{size.Name} thumbnails = make([]string, 0, 1) - } else if m.Square() { + case m.Square(): // Only one thumbnail is required for square images. sizes = []thumb.Name{thumb.Tile224} thumbnails = make([]string, 0, 1) - } else { + default: // Use three thumbnails otherwise (center, left, right). sizes = []thumb.Name{thumb.Tile224, thumb.Left224, thumb.Right224} thumbnails = make([]string, 0, 3) diff --git a/internal/photoprism/purge_options.go b/internal/photoprism/purge_options.go index d4b2ff65d..a104764fc 100644 --- a/internal/photoprism/purge_options.go +++ b/internal/photoprism/purge_options.go @@ -2,7 +2,7 @@ package photoprism import "github.com/photoprism/photoprism/pkg/fs" -// PurgeOptions controls behaviour of the purge worker. +// PurgeOptions controls behavior of the purge worker. type PurgeOptions struct { Path string Ignore fs.Done From 889de682c43735a9695d1584a2585d03c21c796a Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sat, 22 Nov 2025 14:10:56 +0100 Subject: [PATCH 032/195] CI: Apply Go linter recommendations to "internal/event" package #5330 Signed-off-by: Michael Mayer --- internal/event/README.md | 66 ++++++++++++++++++++++++++++++ internal/event/format.go | 1 + internal/event/hub.go | 8 ++++ internal/event/log.go | 2 + internal/event/publish.go | 8 ++++ internal/event/publish_entities.go | 6 +++ internal/event/time.go | 1 + 7 files changed, 92 insertions(+) create mode 100644 internal/event/README.md diff --git a/internal/event/README.md b/internal/event/README.md new file mode 100644 index 000000000..7edddb0c7 --- /dev/null +++ b/internal/event/README.md @@ -0,0 +1,66 @@ +## PhotoPrism — Event System + +**Last Updated:** November 22, 2025 + +### Overview + +`internal/event` provides a lightweight pub/sub hub for in-process notifications. It underpins logging hooks, UI notifications, and domain events (entities created/updated/deleted/archived/restored). The package aliases the `hub` library to keep a stable interface while exposing simple helpers for common topics. + +### Usage + +Publish a custom event: +```go +event.Publish("photos.updated", event.Data{"ids": []string{"p1", "p2"}}) +``` + +Publish localized notifications: +```go +event.SuccessMsg(i18n.MsgImportDone) +event.Warn("low disk space") +``` + +Subscribe to topics: +```go +sub := event.Subscribe("photos.*") +defer event.Unsubscribe(sub) +for msg := range sub.Receiver { + fmt.Printf("topic=%s payload=%v\n", msg.Name, msg.Fields) +} +``` + +Log hook (used by default logger): +```go +hook := event.NewHook(event.SharedHub()) +log.AddHook(hook) +``` + +Entity events: +```go +event.EntitiesUpdated("photos", updatedPhotos) +event.EntitiesDeleted("files", deletedFiles) +``` + +### Package Layout (Code Map) + +- Hub aliases & helpers: `hub.go`, `format.go`, `time.go` +- Logging hook: `log.go` +- Publish helpers: `publish.go`, `publish_entities.go` +- Tests: package-level tests alongside sources + +### Related Packages + +- `internal/photoprism` — core indexing/import flows that emit events. +- `internal/server` — HTTP layer that may consume event notifications. +- `internal/ai/vision` & `internal/ffmpeg` — emit log events via the shared logger. +- External hub library: `github.com/leandro-lugaresi/hub` + +### Testing + +- Lint: `golangci-lint run ./internal/event...` +- Unit tests: `go test ./internal/event/...` (lightweight) + +### Notes + +- Use `SharedHub()` for process-wide subscriptions; `NewHub()` when isolating tests. +- Topic separator is `.`; message separator for rendering is ` › `. +- Keep notifications human-readable; payloads should be small to avoid blocking subscribers. diff --git a/internal/event/format.go b/internal/event/format.go index 1c98aa43f..e377906ae 100644 --- a/internal/event/format.go +++ b/internal/event/format.go @@ -5,6 +5,7 @@ import ( "strings" ) +// MessageSep separates event topic segments when rendered as text. var MessageSep = " › " // Format formats an audit log event. diff --git a/internal/event/hub.go b/internal/event/hub.go index 6258eef60..b6a07150d 100644 --- a/internal/event/hub.go +++ b/internal/event/hub.go @@ -6,19 +6,27 @@ import ( "github.com/leandro-lugaresi/hub" ) +// Hub is an alias for the shared event hub implementation. type Hub = hub.Hub + +// Data represents event payload fields. type Data = hub.Fields + +// Message represents an emitted event message. type Message = hub.Message +// TopicSep separates topic hierarchy segments. const TopicSep = "." var channelCap = 100 var sharedHub = NewHub() +// NewHub creates a new event hub instance. func NewHub() *Hub { return hub.New() } +// SharedHub returns the process-wide shared event hub. func SharedHub() *Hub { return sharedHub } diff --git a/internal/event/log.go b/internal/event/log.go index a198a0fed..ab4a41e60 100644 --- a/internal/event/log.go +++ b/internal/event/log.go @@ -15,6 +15,8 @@ var TextFormatter = &logrus.TextFormatter{ // Log is the global default logger. var Log Logger + +// LogBuffer captures the last log message (primarily for tests). var LogBuffer Buffer // Hook represents a log event hook. diff --git a/internal/event/publish.go b/internal/event/publish.go index fb931222a..9ee92f7b1 100644 --- a/internal/event/publish.go +++ b/internal/event/publish.go @@ -14,38 +14,46 @@ func Publish(event string, data Data) { }) } +// Error publishes an error notification with the given message. func Error(msg string) { Log.Error(strings.ToLower(msg)) Publish("notify.error", Data{"message": msg}) } +// Success publishes a success notification with the given message. func Success(msg string) { Log.Info(strings.ToLower(msg)) Publish("notify.success", Data{"message": msg}) } +// Info publishes an informational notification with the given message. func Info(msg string) { Log.Info(strings.ToLower(msg)) Publish("notify.info", Data{"message": msg}) } +// Warn publishes a warning notification with the given message. func Warn(msg string) { Log.Warn(strings.ToLower(msg)) Publish("notify.warning", Data{"message": msg}) } +// ErrorMsg publishes a localized error notification. func ErrorMsg(id i18n.Message, params ...interface{}) { Error(i18n.Msg(id, params...)) } +// SuccessMsg publishes a localized success notification. func SuccessMsg(id i18n.Message, params ...interface{}) { Success(i18n.Msg(id, params...)) } +// InfoMsg publishes a localized informational notification. func InfoMsg(id i18n.Message, params ...interface{}) { Info(i18n.Msg(id, params...)) } +// WarnMsg publishes a localized warning notification. func WarnMsg(id i18n.Message, params ...interface{}) { Warn(i18n.Msg(id, params...)) } diff --git a/internal/event/publish_entities.go b/internal/event/publish_entities.go index c1c23a75e..b1e3fa8ff 100644 --- a/internal/event/publish_entities.go +++ b/internal/event/publish_entities.go @@ -6,6 +6,7 @@ import ( "github.com/photoprism/photoprism/pkg/rnd" ) +// Entity event action constants. const ( EntityUpdated = "updated" EntityCreated = "created" @@ -44,22 +45,27 @@ func PublishUserEntities(channel, ev string, entities interface{}, userUid strin }) } +// EntitiesUpdated publishes an update notification for the given channel. func EntitiesUpdated(channel string, entities interface{}) { PublishEntities(channel, EntityUpdated, entities) } +// EntitiesCreated publishes a create notification for the given channel. func EntitiesCreated(channel string, entities interface{}) { PublishEntities(channel, EntityCreated, entities) } +// EntitiesDeleted publishes a delete notification for the given channel. func EntitiesDeleted(channel string, entities interface{}) { PublishEntities(channel, EntityDeleted, entities) } +// EntitiesArchived publishes an archive notification for the given channel. func EntitiesArchived(channel string, entities interface{}) { PublishEntities(channel, EntityArchived, entities) } +// EntitiesRestored publishes a restore notification for the given channel. func EntitiesRestored(channel string, entities interface{}) { PublishEntities(channel, EntityRestored, entities) } diff --git a/internal/event/time.go b/internal/event/time.go index d4e45cf00..dd64a8d72 100644 --- a/internal/event/time.go +++ b/internal/event/time.go @@ -2,6 +2,7 @@ package event import "time" +// Day represents a 24-hour duration. const Day = time.Hour * 24 // TimeStamp returns the current timestamp in UTC rounded to seconds. From 4d280e82e202ab0a13cd4ae11a53f0dc00d6b3b0 Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sat, 22 Nov 2025 14:13:59 +0100 Subject: [PATCH 033/195] CI: Update CODEMAP.md #5330 Signed-off-by: Michael Mayer --- CODEMAP.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CODEMAP.md b/CODEMAP.md index 84ea33844..a3c4c673a 100644 --- a/CODEMAP.md +++ b/CODEMAP.md @@ -1,6 +1,6 @@ PhotoPrism — Backend CODEMAP -**Last Updated:** November 21, 2025 +**Last Updated:** November 22, 2025 Purpose - Give agents and contributors a fast, reliable map of where things live and how they fit together, so you can add features, fix bugs, and write tests without spelunking. @@ -40,8 +40,8 @@ High-Level Package Map (Go) - `internal/workers` — background schedulers (index, vision, sync, meta, backup) - `internal/auth` — ACL, sessions, OIDC - `internal/service` — cluster/portal, maps, hub, webdav -- `internal/event` — logging, pub/sub, audit; canonical outcome tokens live in `pkg/log/status` (use helpers like `status.Error(err)` when the sanitized message should be the outcome) -- `internal/ffmpeg`, `internal/thumb`, `internal/meta`, `internal/form`, `internal/mutex` — media, thumbs, metadata, forms, coordination +- `internal/event` — logging, pub/sub, audit; canonical outcome tokens live in `pkg/log/status` (use helpers like `status.Error(err)` when the sanitized message should be the outcome). Docs: `internal/event/README.md`. +- `internal/ffmpeg`, `internal/thumb`, `internal/meta`, `internal/form`, `internal/mutex` — media, thumbs, metadata, forms, coordination. Docs: `internal/ffmpeg/README.md`, `internal/meta/README.md`. - `pkg/*` — reusable utilities (must never import from `internal/*`), e.g. `pkg/clean`, `pkg/enum`, `pkg/fs`, `pkg/txt`, `pkg/http/header` Templates & Static Assets From 90ab65a9b066907b5b8bd198b16dfba681810ed1 Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sat, 22 Nov 2025 14:32:23 +0100 Subject: [PATCH 034/195] Zip: Harden fs.Unzip() implementation in pkg/fs #5330 Signed-off-by: Michael Mayer --- pkg/fs/zip.go | 54 ++++++++++++++--- pkg/fs/zip_test.go | 146 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 192 insertions(+), 8 deletions(-) diff --git a/pkg/fs/zip.go b/pkg/fs/zip.go index a4fc58c31..9a84c307e 100644 --- a/pkg/fs/zip.go +++ b/pkg/fs/zip.go @@ -2,8 +2,10 @@ package fs import ( "archive/zip" + "errors" "fmt" "io" + "math" "os" "path/filepath" "strings" @@ -109,15 +111,24 @@ func Unzip(zipName, dir string, fileSizeLimit, totalSizeLimit int64) (files []st continue } - if totalSizeLimit < 1 { - // Do nothing; - } else if totalSizeLimit = totalSizeLimit - int64(zipFile.UncompressedSize64); totalSizeLimit < 1 { + if zipFile.UncompressedSize64 > uint64(math.MaxInt64) { skipped = append(skipped, zipFile.Name) - totalSizeLimit = 0 continue } - fileName, unzipErr := UnzipFile(zipFile, dir) + if totalSizeLimit > 0 { + entrySize := int64(zipFile.UncompressedSize64) //nolint:gosec // safe: capped by check above + + totalSizeLimit -= entrySize + + if totalSizeLimit < 1 { + skipped = append(skipped, zipFile.Name) + totalSizeLimit = 0 + continue + } + } + + fileName, unzipErr := unzipFileWithLimit(zipFile, dir, fileSizeLimit) if unzipErr != nil { return files, skipped, unzipErr } @@ -130,6 +141,11 @@ func Unzip(zipName, dir string, fileSizeLimit, totalSizeLimit int64) (files []st // UnzipFile writes a file from a zip archive to the target destination. func UnzipFile(f *zip.File, dir string) (fileName string, err error) { + return unzipFileWithLimit(f, dir, 0) +} + +// unzipFileWithLimit writes a file from a zip archive to the target destination while applying a size limit. +func unzipFileWithLimit(f *zip.File, dir string, fileSizeLimit int64) (fileName string, err error) { rc, err := f.Open() if err != nil { return fileName, err @@ -165,9 +181,31 @@ func UnzipFile(f *zip.File, dir string) (fileName string, err error) { defer fd.Close() - _, err = io.Copy(fd, rc) - if err != nil { - return fileName, err + limit := fileSizeLimit + + if limit <= 0 { + switch { + case f.UncompressedSize64 == 0: + limit = math.MaxInt64 + case f.UncompressedSize64 > uint64(math.MaxInt64): + return fileName, fmt.Errorf("zip entry too large") + default: + limit = int64(f.UncompressedSize64) //nolint:gosec // safe: capped above + } + } + + written, copyErr := io.CopyN(fd, rc, limit) + if copyErr != nil && !errors.Is(copyErr, io.EOF) && !errors.Is(copyErr, io.ErrUnexpectedEOF) { + return fileName, copyErr + } + + // Abort if the entry exceeded the configured limit. + if written >= limit && (fileSizeLimit > 0 || f.UncompressedSize64 > 0) { + // Drain a single byte to see if more data remains (indicating truncation). + var b [1]byte + if _, extraErr := rc.Read(b[:]); extraErr == nil { + return fileName, fmt.Errorf("zip entry exceeds limit") + } } return fileName, nil diff --git a/pkg/fs/zip_test.go b/pkg/fs/zip_test.go index 65cdd1236..bacc26df1 100644 --- a/pkg/fs/zip_test.go +++ b/pkg/fs/zip_test.go @@ -2,6 +2,8 @@ package fs import ( "archive/zip" + "encoding/binary" + "math" "os" "path/filepath" "runtime" @@ -148,6 +150,150 @@ func TestUnzip_CreatesDirectoriesAndNestedFiles(t *testing.T) { } } +func TestUnzip_SkipsVeryLargeEntry(t *testing.T) { + dir := t.TempDir() + zipPath := filepath.Join(dir, "huge.zip") + + writeZip64Stub(t, zipPath, "huge.bin", math.MaxUint64) + + files, skipped, err := Unzip(zipPath, filepath.Join(dir, "out"), 0, -1) + assert.NoError(t, err) + assert.Empty(t, files) + assert.Contains(t, skipped, "huge.bin") +} + +// writeZip64Stub writes a minimal ZIP64 archive with one stored entry and custom size values. +func writeZip64Stub(t *testing.T, path, name string, size uint64) { + t.Helper() + + var buf []byte + + bw := func(data []byte) { + buf = append(buf, data...) + } + + writeLE := func(v any) { + var b [8]byte + switch x := v.(type) { + case uint16: + binary.LittleEndian.PutUint16(b[:2], x) + bw(b[:2]) + case uint32: + binary.LittleEndian.PutUint32(b[:4], x) + bw(b[:4]) + case uint64: + binary.LittleEndian.PutUint64(b[:8], x) + bw(b[:8]) + default: + t.Fatalf("unsupported type %T", v) + } + } + + filename := []byte(name) + const ( + sigLocal = 0x04034b50 + sigCentral = 0x02014b50 + sigEnd = 0x06054b50 + ) + + zip64ExtraLen := uint16(4 + 16) // header id + size + two uint64 values + localExtraLen := zip64ExtraLen + centralExtraLen := zip64ExtraLen + + // Local file header + writeLE(uint32(sigLocal)) + writeLE(uint16(45)) // version needed (zip64) + writeLE(uint16(0)) // flags + writeLE(uint16(0)) // method store + writeLE(uint16(0)) // mod time + writeLE(uint16(0)) // mod date + writeLE(uint32(0)) // crc + writeLE(uint32(0xFFFFFFFF)) + writeLE(uint32(0xFFFFFFFF)) + if len(filename) > math.MaxUint16 { + t.Fatalf("filename too long") + } + writeLE(uint16(len(filename))) + writeLE(localExtraLen) + bw(filename) + // zip64 extra + writeLE(uint16(0x0001)) // header id + writeLE(uint16(16)) // data size + writeLE(size) // uncompressed size + writeLE(size) // compressed size + // no file data (size 0) to keep archive tiny + + localLen := len(buf) + + // Central directory header + writeLE(uint32(sigCentral)) + writeLE(uint16(45)) // version made by + writeLE(uint16(45)) // version needed + writeLE(uint16(0)) // flags + writeLE(uint16(0)) // method + writeLE(uint16(0)) // time + writeLE(uint16(0)) // date + writeLE(uint32(0)) // crc + writeLE(uint32(0xFFFFFFFF)) + writeLE(uint32(0xFFFFFFFF)) + if len(filename) > math.MaxUint16 { + t.Fatalf("filename too long") + } + writeLE(uint16(len(filename))) + writeLE(centralExtraLen) + writeLE(uint16(0)) // comment len + writeLE(uint16(0)) // disk start + writeLE(uint16(0)) // int attrs + writeLE(uint32(0)) // ext attrs + writeLE(uint32(0)) // rel offset (zip64 overrides) + bw(filename) + // zip64 extra + writeLE(uint16(0x0001)) + writeLE(uint16(16)) + writeLE(size) // uncompressed + writeLE(size) // compressed + + centralLen := len(buf) - localLen + + // End of central directory (not zip64 EOCD; minimal to satisfy reader) + writeLE(uint32(sigEnd)) + writeLE(uint16(0)) // disk + writeLE(uint16(0)) // start disk + writeLE(uint16(1)) // entries this disk + writeLE(uint16(1)) // total entries + if centralLen > math.MaxUint32 || localLen > math.MaxUint32 { + t.Fatalf("central or local length exceeds uint32") + } + writeLE(uint32(centralLen)) + writeLE(uint32(localLen)) + writeLE(uint16(0)) // comment length + + if err := os.WriteFile(path, buf, 0o600); err != nil { + t.Fatal(err) + } +} + +func TestUnzipFileWithLimit_DetectsOverrun(t *testing.T) { + dir := t.TempDir() + zipPath := filepath.Join(dir, "small.zip") + writeZip(t, zipPath, map[string][]byte{"a.txt": []byte("abc")}) // 3 bytes + + r, err := zip.OpenReader(zipPath) + if err != nil { + t.Fatal(err) + } + defer r.Close() + + if len(r.File) != 1 { + t.Fatalf("expected one file, got %d", len(r.File)) + } + + _, err = unzipFileWithLimit(r.File[0], dir, 1) // limit below actual size + if err == nil { + t.Fatalf("expected limit overrun error") + } +} + func TestZip(t *testing.T) { t.Run("Compressed", func(t *testing.T) { zipDir := filepath.Join(os.TempDir(), "pkg/fs") From 149f5e57314eeea3345f4e8f7a9b992e7c629ddd Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sat, 22 Nov 2025 16:14:43 +0100 Subject: [PATCH 035/195] CI: Apply Go linter recommendations to remaining "pkg/..." code #5330 Signed-off-by: Michael Mayer --- pkg/capture/output.go | 4 +- pkg/capture/stdout.go | 4 +- pkg/capture/time_test.go | 3 +- pkg/checksum/charset.go | 3 ++ pkg/checksum/crc32.go | 1 + pkg/fs/codec.go | 6 +-- pkg/fs/config.go | 2 +- pkg/fs/copy_move.go | 10 ++--- pkg/fs/copy_move_test.go | 18 ++++---- pkg/fs/directories.go | 4 ++ pkg/fs/done.go | 8 +++- pkg/fs/duf/filesystems.go | 1 + pkg/fs/duf/filesystems_linux.go | 2 +- pkg/fs/duf/filters.go | 2 + pkg/fs/duf/groups.go | 3 ++ pkg/fs/duf/mounts.go | 6 +-- pkg/fs/duf/mounts_linux.go | 25 ++++++----- pkg/fs/duf/util.go | 1 + pkg/fs/fastwalk/fastwalk_dirent_fileno.go | 1 - pkg/fs/fastwalk/fastwalk_dirent_ino.go | 2 - pkg/fs/fastwalk/fastwalk_dirent_namlen_bsd.go | 1 - .../fastwalk/fastwalk_dirent_namlen_linux.go | 3 +- pkg/fs/fastwalk/fastwalk_portable.go | 1 - pkg/fs/fastwalk/fastwalk_test.go | 8 ++-- pkg/fs/fastwalk/fastwalk_unix.go | 13 +++--- pkg/fs/file_ext.go | 1 + pkg/fs/file_exts.go | 12 +----- pkg/fs/file_info.go | 1 + pkg/fs/file_types.go | 12 +++--- pkg/fs/file_types_ext.go | 14 +++--- pkg/fs/fileinfo.go | 4 ++ pkg/fs/fs.go | 16 +++---- pkg/fs/fs_test.go | 6 +-- pkg/fs/hash.go | 8 ++-- pkg/fs/id.go | 28 +++++++----- pkg/fs/ignore.go | 3 +- pkg/fs/mime.go | 1 + pkg/fs/readlines.go | 2 +- pkg/fs/symlink.go | 2 +- pkg/fs/walk.go | 20 +++++---- pkg/fs/write.go | 4 +- pkg/fs/write_test.go | 6 +-- pkg/fs/zip.go | 6 +-- pkg/fs/zip_test.go | 14 +++--- pkg/geo/dist.go | 14 +++--- pkg/geo/geo.go | 12 ++++-- pkg/geo/latlng/round.go | 1 + pkg/geo/movement.go | 20 +++++---- pkg/geo/position.go | 1 + pkg/geo/randomize.go | 13 +++++- pkg/geo/s2/token_prefix.go | 1 + pkg/http/header/auth.go | 6 +-- pkg/http/header/auth_test.go | 2 +- pkg/http/header/cache.go | 3 +- pkg/http/header/cdn.go | 12 +++--- pkg/http/header/cidr.go | 5 ++- pkg/http/header/ip.go | 4 +- pkg/http/header/proto.go | 7 ++- pkg/http/header/robots.go | 1 + pkg/http/header/values.go | 4 +- pkg/http/header/webdav.go | 4 +- pkg/http/header/webhook.go | 10 +++-- pkg/http/safe/download.go | 10 ++--- pkg/http/safe/download_redirect_test.go | 2 +- pkg/http/safe/download_test.go | 2 +- pkg/http/safe/options.go | 7 ++- pkg/http/scheme/const.go | 30 +++++++++---- pkg/i18n/i18n.go | 3 ++ pkg/i18n/locales.go | 38 +++++++++++----- pkg/i18n/messages.go | 4 ++ pkg/i18n/response.go | 4 ++ pkg/list/add.go | 11 ++--- pkg/list/attributes.go | 21 ++++----- pkg/list/contains.go | 8 +--- pkg/list/ordered/list.go | 2 +- pkg/list/ordered/map.go | 2 +- pkg/list/ordered/map_test.go | 6 ++- pkg/list/ordered/sync_test.go | 24 ++++++----- pkg/log/dummy/logger.go | 2 + pkg/log/status/error_test.go | 2 +- pkg/media/README.md | 21 ++++----- pkg/media/colors/chroma.go | 2 +- pkg/media/colors/colors.go | 27 ++++++++++++ pkg/media/colors/examples.go | 1 + pkg/media/colors/lightmap.go | 5 ++- pkg/media/colors/lightmap_test.go | 8 ++-- pkg/media/colors/luminance.go | 2 + pkg/media/colors/map.go | 1 + pkg/media/colors/profiles.go | 1 + pkg/media/colors/srgb.go | 3 +- pkg/media/colors/srgb_test.go | 4 +- pkg/media/data_url.go | 4 +- pkg/media/data_url_test.go | 3 +- pkg/media/orientation.go | 4 +- pkg/media/preview.go | 3 ++ pkg/media/projection/types.go | 19 +++++--- pkg/media/source.go | 5 ++- pkg/media/video/brands.go | 2 +- pkg/media/video/chunk.go | 2 +- pkg/media/video/codec_profile.go | 7 ++- pkg/media/video/codecs.go | 1 + pkg/media/video/probe.go | 8 ++-- pkg/media/video/reader.go | 2 +- pkg/react/reactions.go | 43 +++++++++++++------ pkg/time/tz/offset.go | 3 +- pkg/time/tz/time.go | 4 +- pkg/vector/alg/common.go | 13 ++++-- pkg/vector/alg/csv_importer.go | 6 +-- pkg/vector/alg/dbscan.go | 18 ++++---- pkg/vector/alg/errors.go | 17 ++++---- pkg/vector/alg/json_importer.go | 6 +-- pkg/vector/alg/kmeans.go | 27 ++++++------ pkg/vector/alg/kmeans_estimator.go | 1 + pkg/vector/alg/optics.go | 21 +++++---- pkg/vector/const.go | 2 + pkg/vector/values.go | 6 +-- 116 files changed, 541 insertions(+), 351 deletions(-) diff --git a/pkg/capture/output.go b/pkg/capture/output.go index d1e3b56d2..a6a2f27d0 100644 --- a/pkg/capture/output.go +++ b/pkg/capture/output.go @@ -26,10 +26,10 @@ func Output(f func()) string { }() f() - w.Close() + _ = w.Close() var buf bytes.Buffer - io.Copy(&buf, r) + _, _ = io.Copy(&buf, r) return buf.String() } diff --git a/pkg/capture/stdout.go b/pkg/capture/stdout.go index 34dee7874..461588041 100644 --- a/pkg/capture/stdout.go +++ b/pkg/capture/stdout.go @@ -20,10 +20,10 @@ func Stdout(f func()) string { }() f() - w.Close() + _ = w.Close() var buf bytes.Buffer - io.Copy(&buf, r) + _, _ = io.Copy(&buf, r) return buf.String() } diff --git a/pkg/capture/time_test.go b/pkg/capture/time_test.go index a8c1651f2..c26189fb9 100644 --- a/pkg/capture/time_test.go +++ b/pkg/capture/time_test.go @@ -1,7 +1,6 @@ package capture import ( - "fmt" "testing" "time" @@ -11,6 +10,6 @@ import ( func TestTime(t *testing.T) { start := time.Now() time.Sleep(1 * time.Millisecond) - result := Time(start, fmt.Sprintf("%s", "Successful test")) + result := Time(start, "Successful test") assert.Contains(t, result, "Successful test [") } diff --git a/pkg/checksum/charset.go b/pkg/checksum/charset.go index c6cbc2a11..b56133f50 100644 --- a/pkg/checksum/charset.go +++ b/pkg/checksum/charset.go @@ -1,8 +1,11 @@ package checksum const ( + // CharsetBase10 contains digits for base10 encoding. CharsetBase10 = "0123456789" + // CharsetBase36 contains lowercase alphanumerics for base36. CharsetBase36 = "abcdefghijklmnopqrstuvwxyz0123456789" + // CharsetBase62 contains mixed-case alphanumerics for base62. CharsetBase62 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" ) diff --git a/pkg/checksum/crc32.go b/pkg/checksum/crc32.go index d3b92f7e0..19b0d2c9a 100644 --- a/pkg/checksum/crc32.go +++ b/pkg/checksum/crc32.go @@ -5,6 +5,7 @@ import ( "hash/crc32" ) +// Crc32Castagnoli provides the Castagnoli polynomial table for CRC32. var Crc32Castagnoli = crc32.MakeTable(crc32.Castagnoli) // Crc32 returns the CRC-32 checksum of data using the crc32.IEEE polynomial. diff --git a/pkg/fs/codec.go b/pkg/fs/codec.go index 6fa2450f0..57ae3795d 100644 --- a/pkg/fs/codec.go +++ b/pkg/fs/codec.go @@ -2,7 +2,7 @@ package fs // Required file format decoders and encoders. import ( - _ "image/gif" - _ "image/jpeg" - _ "image/png" + _ "image/gif" // register GIF decoder + _ "image/jpeg" // register JPEG decoder + _ "image/png" // register PNG decoder ) diff --git a/pkg/fs/config.go b/pkg/fs/config.go index 41cf11f2a..70cf8ac73 100644 --- a/pkg/fs/config.go +++ b/pkg/fs/config.go @@ -17,7 +17,7 @@ func ConfigFilePath(configPath, baseName, defaultExt string) string { return "" } - // Search file in current directory if configPath is emtpy. + // Search file in current directory if configPath is empty. if configPath == "" { if dir, err := os.Getwd(); err == nil && dir != "" { configPath = dir diff --git a/pkg/fs/copy_move.go b/pkg/fs/copy_move.go index 33a89a03b..d16f5952d 100644 --- a/pkg/fs/copy_move.go +++ b/pkg/fs/copy_move.go @@ -55,7 +55,7 @@ func Copy(src, dest string, force bool) (err error) { return err } - thisFile, err := os.Open(src) + thisFile, err := os.Open(src) //nolint:gosec // src is validated by callers if err != nil { return err @@ -64,7 +64,7 @@ func Copy(src, dest string, force bool) (err error) { defer thisFile.Close() // Open destination for write; create or truncate to avoid trailing bytes - destFile, err := os.OpenFile(dest, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, ModeFile) + destFile, err := os.OpenFile(dest, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, ModeFile) //nolint:gosec // dest is derived from validated input if err != nil { return err @@ -136,9 +136,5 @@ func Move(src, dest string, force bool) (err error) { return err } - if err = os.Remove(src); err != nil { - return err - } - - return nil + return os.Remove(src) } diff --git a/pkg/fs/copy_move_test.go b/pkg/fs/copy_move_test.go index d9bfb17c4..3418b23ca 100644 --- a/pkg/fs/copy_move_test.go +++ b/pkg/fs/copy_move_test.go @@ -17,7 +17,7 @@ func TestCopy_NewDestination_Succeeds(t *testing.T) { err := Copy(src, dst, false) assert.NoError(t, err) - b, _ := os.ReadFile(dst) + b, _ := os.ReadFile(dst) //nolint:gosec // test helper reads temp file assert.Equal(t, "hello", string(b)) } @@ -31,7 +31,7 @@ func TestCopy_ExistingNonEmpty_NoForce_Error(t *testing.T) { err := Copy(src, dst, false) assert.Error(t, err) - b, _ := os.ReadFile(dst) + b, _ := os.ReadFile(dst) //nolint:gosec // test helper reads temp file assert.Equal(t, "existing", string(b)) } @@ -46,7 +46,7 @@ func TestCopy_ExistingNonEmpty_Force_TruncatesAndOverwrites(t *testing.T) { err := Copy(src, dst, true) assert.NoError(t, err) - b, _ := os.ReadFile(dst) + b, _ := os.ReadFile(dst) //nolint:gosec // test helper reads temp file assert.Equal(t, "short", string(b)) } @@ -60,7 +60,7 @@ func TestCopy_ExistingEmpty_NoForce_AllowsReplace(t *testing.T) { err := Copy(src, dst, false) assert.NoError(t, err) - b, _ := os.ReadFile(dst) + b, _ := os.ReadFile(dst) //nolint:gosec // test helper reads temp file assert.Equal(t, "data", string(b)) } @@ -93,7 +93,7 @@ func TestMove_NewDestination_Succeeds(t *testing.T) { // Source is removed; dest contains data _, serr := os.Stat(src) assert.True(t, os.IsNotExist(serr)) - b, _ := os.ReadFile(dst) + b, _ := os.ReadFile(dst) //nolint:gosec // test helper reads temp file assert.Equal(t, "hello", string(b)) } @@ -108,8 +108,8 @@ func TestMove_ExistingNonEmpty_NoForce_Error(t *testing.T) { err := Move(src, dst, false) assert.Error(t, err) // Verify both files unchanged - bsrc, _ := os.ReadFile(src) - bdst, _ := os.ReadFile(dst) + bsrc, _ := os.ReadFile(src) //nolint:gosec // test helper reads temp file + bdst, _ := os.ReadFile(dst) //nolint:gosec // test helper reads temp file assert.Equal(t, "src", string(bsrc)) assert.Equal(t, "dst", string(bdst)) } @@ -126,7 +126,7 @@ func TestMove_ExistingEmpty_NoForce_AllowsReplace(t *testing.T) { assert.NoError(t, err) _, serr := os.Stat(src) assert.True(t, os.IsNotExist(serr)) - bdst, _ := os.ReadFile(dst) + bdst, _ := os.ReadFile(dst) //nolint:gosec // test helper reads temp file assert.Equal(t, "src", string(bdst)) } @@ -142,7 +142,7 @@ func TestMove_ExistingNonEmpty_Force_Succeeds(t *testing.T) { assert.NoError(t, err) _, serr := os.Stat(src) assert.True(t, os.IsNotExist(serr)) - bdst, _ := os.ReadFile(dst) + bdst, _ := os.ReadFile(dst) //nolint:gosec // test helper reads temp file assert.Equal(t, "AAA", string(bdst)) } diff --git a/pkg/fs/directories.go b/pkg/fs/directories.go index d3ac7993f..2b5b0f0e7 100644 --- a/pkg/fs/directories.go +++ b/pkg/fs/directories.go @@ -10,6 +10,7 @@ import ( "github.com/photoprism/photoprism/pkg/fs/fastwalk" ) +// OriginalPaths lists default Originals search paths. var OriginalPaths = []string{ "/photoprism/storage/media/originals", "/photoprism/media/originals", @@ -76,6 +77,7 @@ var OriginalPaths = []string{ "/var/lib/photoprism/originals", } +// ImportPaths lists default Import search paths. var ImportPaths = []string{ "/photoprism/storage/media/import", "/photoprism/media/import", @@ -110,6 +112,7 @@ var ImportPaths = []string{ "/var/lib/photoprism/import", } +// AssetPaths lists default asset paths. var AssetPaths = []string{ "/opt/photoprism/assets", "/photoprism/assets", @@ -120,6 +123,7 @@ var AssetPaths = []string{ "/var/lib/photoprism/assets", } +// ModelsPaths lists default model lookup paths. var ModelsPaths = []string{ "/opt/photoprism/assets/models", "/photoprism/assets/models", diff --git a/pkg/fs/done.go b/pkg/fs/done.go index 3367b5c5a..968fca3b9 100644 --- a/pkg/fs/done.go +++ b/pkg/fs/done.go @@ -1,12 +1,16 @@ package fs +// Status indicates whether a path was seen or processed. type Status int8 const ( - Found Status = 1 + // Found marks a path as seen. + Found Status = 1 + // Processed marks a path as fully handled. Processed Status = 2 ) +// Done stores per-path processing state. type Done map[string]Status // Processed counts the number of processed files. @@ -22,10 +26,12 @@ func (d Done) Processed() int { return count } +// Exists reports whether any status is recorded. func (s Status) Exists() bool { return s > 0 } +// Processed returns true if the path was marked as processed. func (s Status) Processed() bool { return s >= Processed } diff --git a/pkg/fs/duf/filesystems.go b/pkg/fs/duf/filesystems.go index 8dc028090..bd7974e17 100644 --- a/pkg/fs/duf/filesystems.go +++ b/pkg/fs/duf/filesystems.go @@ -6,6 +6,7 @@ import ( "strings" ) +// nolint:unused // kept for potential platform-specific filesystem filtering func findMounts(mounts []Mount, path string) ([]Mount, error) { var err error path, err = filepath.Abs(path) diff --git a/pkg/fs/duf/filesystems_linux.go b/pkg/fs/duf/filesystems_linux.go index cc12b45cb..8b484470b 100644 --- a/pkg/fs/duf/filesystems_linux.go +++ b/pkg/fs/duf/filesystems_linux.go @@ -4,7 +4,7 @@ package duf import "strings" -//nolint:revive,deadcode +//nolint:revive // constants kept for reference in filesystem detection const ( // man statfs ADFS_SUPER_MAGIC = 0xadf5 diff --git a/pkg/fs/duf/filters.go b/pkg/fs/duf/filters.go index 461efd5bb..b861d935d 100644 --- a/pkg/fs/duf/filters.go +++ b/pkg/fs/duf/filters.go @@ -10,8 +10,10 @@ var ( onlyMp = "" ) +// FilterValues holds a set of filter strings. type FilterValues map[string]struct{} +// NewFilterValues converts strings or comma-separated lists into a FilterValues set. func NewFilterValues(s ...string) FilterValues { if len(s) == 0 { return make(FilterValues) diff --git a/pkg/fs/duf/groups.go b/pkg/fs/duf/groups.go index 0bb789120..58c0e7e75 100644 --- a/pkg/fs/duf/groups.go +++ b/pkg/fs/duf/groups.go @@ -5,11 +5,14 @@ import ( ) var ( + // nolint:unused // kept for potential future grouping logic extensions groups = []string{LocalDevice, NetworkDevice, FuseDevice, SpecialDevice, LoopsDevice, BindsMount} ) +// GroupedMounts maps device types to their mounts. type GroupedMounts map[string][]Mount +// GroupMounts groups mounts by device type, applying the given filters. func GroupMounts(m []Mount, filters FilterOptions) GroupedMounts { deviceMounts := make(GroupedMounts) hasOnlyDevices := len(filters.OnlyDevices) != 0 diff --git a/pkg/fs/duf/mounts.go b/pkg/fs/duf/mounts.go index f17193edf..66f14cd87 100644 --- a/pkg/fs/duf/mounts.go +++ b/pkg/fs/duf/mounts.go @@ -26,7 +26,7 @@ type Mount struct { } func readLines(filename string) ([]string, error) { - file, err := os.Open(filename) + file, err := os.Open(filename) //nolint:gosec // filename comes from platform mountinfo source if err != nil { return nil, err } @@ -49,7 +49,7 @@ func unescapeFstab(path string) string { return escaped } -//nolint:deadcode,unused // used on BSD +//nolint:unused // used on BSD func byteToString(orig []byte) string { n := -1 l := -1 @@ -73,7 +73,7 @@ func byteToString(orig []byte) string { return string(orig[l:n]) } -//nolint:deadcode,unused // used on OpenBSD +//nolint:unused // used on OpenBSD func intToString(orig []int8) string { ret := make([]byte, len(orig)) size := -1 diff --git a/pkg/fs/duf/mounts_linux.go b/pkg/fs/duf/mounts_linux.go index 179e163e2..0983c342e 100644 --- a/pkg/fs/duf/mounts_linux.go +++ b/pkg/fs/duf/mounts_linux.go @@ -18,13 +18,13 @@ const ( // (0) (1) (2) (3) (4) (5) (6) (7) (8) (9) (10) // // (0) mount ID: unique identifier of the mount (may be reused after umount). - //mountinfoMountID = 0 + // mountinfoMountID = 0 // (1) parent ID: ID of parent (or of self for the top of the mount tree). - //mountinfoParentID = 1 + // mountinfoParentID = 1 // (2) major:minor: value of st_dev for files on filesystem. - //mountinfoMajorMinor = 2 + // mountinfoMajorMinor = 2 // (3) root: root of the mount within the filesystem. - //mountinfoRoot = 3 + // mountinfoRoot = 3 // (4) mount point: mount point relative to the process's root. mountinfoMountPoint = 4 // (5) mount options: per mount options. @@ -32,13 +32,13 @@ const ( // (6) optional fields: zero or more fields terminated by "-". mountinfoOptionalFields = 6 // (7) separator between optional fields. - //mountinfoSeparator = 7 + // mountinfoSeparator = 7 // (8) filesystem type: name of filesystem of the form. mountinfoFsType = 8 // (9) mount source: filesystem specific information or "none". mountinfoMountSource = 9 // (10) super options: per super block options. - //mountinfoSuperOptions = 10 + // mountinfoSuperOptions = 10 ) // Stat returns the mountpoint's stat information. @@ -70,6 +70,11 @@ func mounts() ([]Mount, []string, error) { } // blockDeviceID := fields[mountinfoMountID] + if len(fields) <= mountinfoMountSource { + warnings = append(warnings, fmt.Sprintf("incomplete mountinfo line: %s", line)) + continue + } + mountPoint := fields[mountinfoMountPoint] mountOpts := fields[mountinfoMountOpts] fstype := fields[mountinfoFsType] @@ -93,14 +98,14 @@ func mounts() ([]Mount, []string, error) { Type: fsTypeMap[int64(stat.Type)], //nolint:unconvert Opts: mountOpts, Metadata: stat, - Total: (uint64(stat.Blocks) * uint64(stat.Bsize)), //nolint:unconvert - Free: (uint64(stat.Bavail) * uint64(stat.Bsize)), //nolint:unconvert - Used: (uint64(stat.Blocks) - uint64(stat.Bfree)) * uint64(stat.Bsize), //nolint:unconvert + Total: (uint64(stat.Blocks) * uint64(stat.Bsize)), //nolint:unconvert,gosec // stat values are kernel-provided + Free: (uint64(stat.Bavail) * uint64(stat.Bsize)), //nolint:unconvert,gosec + Used: (uint64(stat.Blocks) - uint64(stat.Bfree)) * uint64(stat.Bsize), //nolint:unconvert,gosec Inodes: stat.Files, InodesFree: stat.Ffree, InodesUsed: stat.Files - stat.Ffree, Blocks: uint64(stat.Blocks), //nolint:unconvert - BlockSize: uint64(stat.Bsize), + BlockSize: uint64(stat.Bsize), //nolint:gosec // kernel-provided value fits uint64 } d.DeviceType = deviceType(d) diff --git a/pkg/fs/duf/util.go b/pkg/fs/duf/util.go index c971f762c..8a073ba48 100644 --- a/pkg/fs/duf/util.go +++ b/pkg/fs/duf/util.go @@ -25,6 +25,7 @@ func parseCommaSeparatedValues(values string) FilterValues { } // validateGroups validates the parsed group maps. +// nolint:unused // reserved for future validation hooks func validateGroups(m FilterValues) error { for k := range m { found := slices.Contains(groups, k) diff --git a/pkg/fs/fastwalk/fastwalk_dirent_fileno.go b/pkg/fs/fastwalk/fastwalk_dirent_fileno.go index d58595dbd..d393c55a8 100644 --- a/pkg/fs/fastwalk/fastwalk_dirent_fileno.go +++ b/pkg/fs/fastwalk/fastwalk_dirent_fileno.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build freebsd || openbsd || netbsd -// +build freebsd openbsd netbsd package fastwalk diff --git a/pkg/fs/fastwalk/fastwalk_dirent_ino.go b/pkg/fs/fastwalk/fastwalk_dirent_ino.go index ea02b9ebf..80ca3a69f 100644 --- a/pkg/fs/fastwalk/fastwalk_dirent_ino.go +++ b/pkg/fs/fastwalk/fastwalk_dirent_ino.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (linux || darwin) && !appengine -// +build linux darwin -// +build !appengine package fastwalk diff --git a/pkg/fs/fastwalk/fastwalk_dirent_namlen_bsd.go b/pkg/fs/fastwalk/fastwalk_dirent_namlen_bsd.go index d5c9c321e..cce188e10 100644 --- a/pkg/fs/fastwalk/fastwalk_dirent_namlen_bsd.go +++ b/pkg/fs/fastwalk/fastwalk_dirent_namlen_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || freebsd || openbsd || netbsd -// +build darwin freebsd openbsd netbsd package fastwalk diff --git a/pkg/fs/fastwalk/fastwalk_dirent_namlen_linux.go b/pkg/fs/fastwalk/fastwalk_dirent_namlen_linux.go index c82e57df8..be8cc9f19 100644 --- a/pkg/fs/fastwalk/fastwalk_dirent_namlen_linux.go +++ b/pkg/fs/fastwalk/fastwalk_dirent_namlen_linux.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && !appengine -// +build linux,!appengine package fastwalk @@ -15,7 +14,7 @@ import ( func direntNamlen(dirent *syscall.Dirent) uint64 { const fixedHdr = uint16(unsafe.Offsetof(syscall.Dirent{}.Name)) - nameBuf := (*[unsafe.Sizeof(dirent.Name)]byte)(unsafe.Pointer(&dirent.Name[0])) + nameBuf := (*[unsafe.Sizeof(dirent.Name)]byte)(unsafe.Pointer(&dirent.Name[0])) //nolint:gosec // bounded by Dirent name buffer size const nameBufLen = uint16(len(nameBuf)) limit := dirent.Reclen - fixedHdr if limit > nameBufLen { diff --git a/pkg/fs/fastwalk/fastwalk_portable.go b/pkg/fs/fastwalk/fastwalk_portable.go index 4ad7fd7d6..65cd20323 100644 --- a/pkg/fs/fastwalk/fastwalk_portable.go +++ b/pkg/fs/fastwalk/fastwalk_portable.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build appengine || (!linux && !darwin && !freebsd && !openbsd && !netbsd) -// +build appengine !linux,!darwin,!freebsd,!openbsd,!netbsd package fastwalk diff --git a/pkg/fs/fastwalk/fastwalk_test.go b/pkg/fs/fastwalk/fastwalk_test.go index 2e3c04c03..30835ad50 100644 --- a/pkg/fs/fastwalk/fastwalk_test.go +++ b/pkg/fs/fastwalk/fastwalk_test.go @@ -8,6 +8,7 @@ import ( "bytes" "flag" "fmt" + "go/build" "os" "path/filepath" "reflect" @@ -43,14 +44,14 @@ func testFastWalk(t *testing.T, files map[string]string, callback func(path stri for path, contents := range files { file := filepath.Join(tempdir, "/src", path) - if err = os.MkdirAll(filepath.Dir(file), 0755); err != nil { + if err = os.MkdirAll(filepath.Dir(file), 0o750); err != nil { t.Fatal(err) } if strings.HasPrefix(contents, "LINK:") { err = os.Symlink(strings.TrimPrefix(contents, "LINK:"), file) } else { - err = os.WriteFile(file, []byte(contents), 0644) + err = os.WriteFile(file, []byte(contents), 0o600) } if err != nil { @@ -229,7 +230,8 @@ func TestFastWalk_TraverseSymlink(t *testing.T) { }) } -var benchDir = flag.String("benchdir", runtime.GOROOT(), "The directory to scan for BenchmarkFastWalk") +// Default to build.Default.GOROOT to avoid runtime.GOROOT deprecation. +var benchDir = flag.String("benchdir", build.Default.GOROOT, "The directory to scan for BenchmarkFastWalk") func BenchmarkFastWalk(b *testing.B) { b.ReportAllocs() diff --git a/pkg/fs/fastwalk/fastwalk_unix.go b/pkg/fs/fastwalk/fastwalk_unix.go index e4edb5cde..f2f61b64d 100644 --- a/pkg/fs/fastwalk/fastwalk_unix.go +++ b/pkg/fs/fastwalk/fastwalk_unix.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (linux || darwin || freebsd || openbsd || netbsd) && !appengine -// +build linux darwin freebsd openbsd netbsd -// +build !appengine package fastwalk @@ -79,7 +77,7 @@ func readDir(dirName string, fn func(dirName, entName string, typ os.FileMode) e func parseDirEnt(buf []byte) (consumed int, name string, typ os.FileMode) { // golang.org/issue/37269 dirent := &syscall.Dirent{} - copy((*[unsafe.Sizeof(syscall.Dirent{})]byte)(unsafe.Pointer(dirent))[:], buf) + copy((*[unsafe.Sizeof(syscall.Dirent{})]byte)(unsafe.Pointer(dirent))[:], buf) //nolint:gosec // unsafe needed for fast directory walk if v := unsafe.Offsetof(dirent.Reclen) + unsafe.Sizeof(dirent.Reclen); uintptr(len(buf)) < v { panic(fmt.Sprintf("buf size of %d smaller than dirent header size %d", len(buf), v)) } @@ -114,15 +112,16 @@ func parseDirEnt(buf []byte) (consumed int, name string, typ os.FileMode) { return } - nameBuf := (*[unsafe.Sizeof(dirent.Name)]byte)(unsafe.Pointer(&dirent.Name[0])) + nameBuf := (*[unsafe.Sizeof(dirent.Name)]byte)(unsafe.Pointer(&dirent.Name[0])) //nolint:gosec // bounded by dirent name buffer nameLen := direntNamlen(dirent) // Special cases for common things: - if nameLen == 1 && nameBuf[0] == '.' { + switch { + case nameLen == 1 && nameBuf[0] == '.': name = "." - } else if nameLen == 2 && nameBuf[0] == '.' && nameBuf[1] == '.' { + case nameLen == 2 && nameBuf[0] == '.' && nameBuf[1] == '.': name = ".." - } else { + default: name = string(nameBuf[:nameLen]) } return diff --git a/pkg/fs/file_ext.go b/pkg/fs/file_ext.go index 1ad82c5cd..379f054e3 100644 --- a/pkg/fs/file_ext.go +++ b/pkg/fs/file_ext.go @@ -5,6 +5,7 @@ import ( "strings" ) +// Common file extensions used throughout PhotoPrism. const ( ExtNone = "" ExtLocal = ".local" diff --git a/pkg/fs/file_exts.go b/pkg/fs/file_exts.go index b6e334af6..a1628e24b 100644 --- a/pkg/fs/file_exts.go +++ b/pkg/fs/file_exts.go @@ -197,20 +197,12 @@ func (m FileExtensions) Types(noUppercase bool) TypesExt { if noUppercase { for ext, t := range m { - if _, ok := result[t]; ok { - result[t] = append(result[t], ext) - } else { - result[t] = []string{ext} - } + result[t] = append(result[t], ext) } } else { for ext, t := range m { extUpper := strings.ToUpper(ext) - if _, ok := result[t]; ok { - result[t] = append(result[t], ext, extUpper) - } else { - result[t] = []string{ext, extUpper} - } + result[t] = append(result[t], ext, extUpper) } } diff --git a/pkg/fs/file_info.go b/pkg/fs/file_info.go index b171c6ed5..09b761f76 100644 --- a/pkg/fs/file_info.go +++ b/pkg/fs/file_info.go @@ -1,5 +1,6 @@ package fs +// TypeMap maps file types to a representative extension string. type TypeMap map[Type]string // TypeInfo contains human-readable descriptions for supported file formats diff --git a/pkg/fs/file_types.go b/pkg/fs/file_types.go index 1158fe456..76313c1df 100644 --- a/pkg/fs/file_types.go +++ b/pkg/fs/file_types.go @@ -1,13 +1,13 @@ package fs import ( - _ "image/gif" - _ "image/jpeg" - _ "image/png" + _ "image/gif" // register GIF decoder + _ "image/jpeg" // register JPEG decoder + _ "image/png" // register PNG decoder - _ "golang.org/x/image/bmp" - _ "golang.org/x/image/tiff" - _ "golang.org/x/image/webp" + _ "golang.org/x/image/bmp" // register BMP decoder + _ "golang.org/x/image/tiff" // register TIFF decoder + _ "golang.org/x/image/webp" // register WEBP decoder ) // Supported archive file types: diff --git a/pkg/fs/file_types_ext.go b/pkg/fs/file_types_ext.go index b5fb6eca9..b65001abb 100644 --- a/pkg/fs/file_types_ext.go +++ b/pkg/fs/file_types_ext.go @@ -1,13 +1,13 @@ package fs import ( - _ "image/gif" - _ "image/jpeg" - _ "image/png" + _ "image/gif" // register GIF decoder + _ "image/jpeg" // register JPEG decoder + _ "image/png" // register PNG decoder - _ "golang.org/x/image/bmp" - _ "golang.org/x/image/tiff" - _ "golang.org/x/image/webp" + _ "golang.org/x/image/bmp" // register BMP decoder + _ "golang.org/x/image/tiff" // register TIFF decoder + _ "golang.org/x/image/webp" // register WEBP decoder ) // TypesExt maps standard formats to file extensions. @@ -15,4 +15,6 @@ type TypesExt map[Type][]string // FileTypes contains the default file type extensions. var FileTypes = Extensions.Types(ignoreCase) + +// FileTypesLower contains lowercase extensions for case-insensitive lookup. var FileTypesLower = Extensions.Types(true) diff --git a/pkg/fs/fileinfo.go b/pkg/fs/fileinfo.go index 55e7315ad..7eda11f90 100644 --- a/pkg/fs/fileinfo.go +++ b/pkg/fs/fileinfo.go @@ -65,6 +65,7 @@ func WebFileInfo(file webdav.FileInfo, dir string) FileInfo { return result } +// FileInfos is a slice helper for bulk file info operations. type FileInfos []FileInfo func (infos FileInfos) Len() int { return len(infos) } @@ -72,6 +73,8 @@ func (infos FileInfos) Swap(i, j int) { infos[i], infos[j] = infos[j], infos[i] func (infos FileInfos) Less(i, j int) bool { return strings.Compare(infos[i].Abs, infos[j].Abs) == -1 } + +// Abs returns absolute file paths for all file infos. func (infos FileInfos) Abs() (result []string) { for _, info := range infos { result = append(result, info.Abs) @@ -80,6 +83,7 @@ func (infos FileInfos) Abs() (result []string) { return result } +// NewFileInfos builds FileInfos from os.FileInfo with directory prefix. func NewFileInfos(infos []os.FileInfo, dir string) FileInfos { var result FileInfos diff --git a/pkg/fs/fs.go b/pkg/fs/fs.go index 3800b789f..a7cb96278 100644 --- a/pkg/fs/fs.go +++ b/pkg/fs/fs.go @@ -38,9 +38,12 @@ import ( var ignoreCase bool const ( + // PathSeparator is the filesystem path separator for the current OS. PathSeparator = string(filepath.Separator) - Home = "~" - HomePath = Home + PathSeparator + // Home represents the tilde shorthand for the user's home directory. + Home = "~" + // HomePath expands Home with a trailing separator. + HomePath = Home + PathSeparator ) // Stat returns the os.FileInfo for the given file path, or an error if it does not exist. @@ -214,7 +217,7 @@ func Download(fileName string, url string) error { // DirIsEmpty returns true if a directory is empty. func DirIsEmpty(path string) bool { - f, err := os.Open(path) + f, err := os.Open(path) //nolint:gosec // path provided by caller; intended to access filesystem if err != nil { return false @@ -223,10 +226,5 @@ func DirIsEmpty(path string) bool { defer f.Close() _, err = f.Readdirnames(1) - - if err == io.EOF { - return true - } - - return false + return err == io.EOF } diff --git a/pkg/fs/fs_test.go b/pkg/fs/fs_test.go index 0126dbf59..f9947eb47 100644 --- a/pkg/fs/fs_test.go +++ b/pkg/fs/fs_test.go @@ -130,7 +130,7 @@ func TestDirIsEmpty(t *testing.T) { assert.Equal(t, false, DirIsEmpty("./xxx")) }) t.Run("EmptyDir", func(t *testing.T) { - if err := os.Mkdir("./testdata/emptyDir", 0777); err != nil { + if err := os.Mkdir("./testdata/emptyDir", 0o750); err != nil { t.Fatal(err) } defer os.RemoveAll("./testdata/emptyDir") @@ -168,12 +168,12 @@ func TestDownload_SuccessAndErrors(t *testing.T) { dir := t.TempDir() goodPath := filepath.Join(dir, "sub", "file.txt") - badPath := filepath.Join("file.txt") // invalid path according to Download + badPath := "file.txt" // invalid path according to Download // Success err := Download(goodPath, tsOK.URL) assert.NoError(t, err) - b, rerr := os.ReadFile(goodPath) + b, rerr := os.ReadFile(goodPath) //nolint:gosec // test helper reads temp file assert.NoError(t, rerr) assert.Equal(t, "hello world", string(b)) diff --git a/pkg/fs/hash.go b/pkg/fs/hash.go index 8b4720a20..ffa859f22 100644 --- a/pkg/fs/hash.go +++ b/pkg/fs/hash.go @@ -1,7 +1,7 @@ package fs import ( - "crypto/sha1" + "crypto/sha1" //nolint:gosec // SHA1 retained for legacy hash compatibility "encoding/hex" "hash/crc32" "io" @@ -14,7 +14,7 @@ import ( func Hash(fileName string) string { var result []byte - file, err := os.Open(fileName) + file, err := os.Open(fileName) //nolint:gosec // caller-controlled path; intended file read if err != nil { return "" @@ -22,7 +22,7 @@ func Hash(fileName string) string { defer file.Close() - hash := sha1.New() + hash := sha1.New() //nolint:gosec // legacy SHA1 hashes retained for compatibility if _, err := io.Copy(hash, file); err != nil { return "" @@ -35,7 +35,7 @@ func Hash(fileName string) string { func Checksum(fileName string) string { var result []byte - file, err := os.Open(fileName) + file, err := os.Open(fileName) //nolint:gosec // caller-controlled path; intended file read if err != nil { return "" diff --git a/pkg/fs/id.go b/pkg/fs/id.go index 73ac7762d..feadd6c34 100644 --- a/pkg/fs/id.go +++ b/pkg/fs/id.go @@ -6,9 +6,14 @@ import ( "github.com/photoprism/photoprism/pkg/rnd" ) -var DscNameRegexp = regexp.MustCompile("\\D{3}[\\d_]\\d{4,8}_?\\d{0,6}_?\\d{0,6}[\\.jpgJPGXx]{0,4}") +// DscNameRegexp matches DSLR-like file names. +var DscNameRegexp = regexp.MustCompile(`\D{3}[\d_]\d{4,8}_?\d{0,6}_?\d{0,6}[\.jpgJPGXx]{0,4}`) + +// UniqueNameRegexp matches generated unique names. var UniqueNameRegexp = regexp.MustCompile("[a-f0-9]{8,16}_[a-f0-9]{6,16}_[A-Za-z0-9]{1,20}_?[A-Za-z0-9]{0,4}") // Example: 8263987746_d0a6055c58_o -var UUIDNameRegexp = regexp.MustCompile("[A-Fa-f0-9\\-]{16,36}_?[A-Za-z0-9_]{0,20}") // Example: 8263987746_d0a6055c58_o + +// UUIDNameRegexp matches names prefixed with UUIDs. +var UUIDNameRegexp = regexp.MustCompile(`[A-Fa-f0-9\-]{16,36}_?[A-Za-z0-9_]{0,20}`) // Example: 8263987746_d0a6055c58_o // IsInt tests if the file base is an integer number. func IsInt(s string) bool { @@ -76,21 +81,22 @@ func IsGenerated(fileName string) bool { base := BasePrefix(fileName, false) - if IsAsciiID(base) { + switch { + case IsAsciiID(base): return true - } else if IsHash(base) { + case IsHash(base): return true - } else if IsInt(base) { + case IsInt(base): return true - } else if IsDscName(base) { + case IsDscName(base): return true - } else if IsUniqueName(base) { + case IsUniqueName(base): return true - } else if rnd.IsUnique(base, 0) { + case rnd.IsUnique(base, 0): return true - } else if IsCanonical(base) { + case IsCanonical(base): return true + default: + return false } - - return false } diff --git a/pkg/fs/ignore.go b/pkg/fs/ignore.go index 2339695a9..c0ee0f9a0 100644 --- a/pkg/fs/ignore.go +++ b/pkg/fs/ignore.go @@ -8,6 +8,7 @@ import ( "sync" ) +// IgnoreLogFunc logs ignored file names. type IgnoreLogFunc func(fileName string) // IgnorePattern represents a name pattern to be ignored. @@ -171,7 +172,7 @@ func (l *IgnoreList) Ignore(name string) bool { baseName := filepath.Base(name) // Change name to lowercase for case-insensitive comparison. - if l.caseSensitive == false { + if !l.caseSensitive { dir = strings.ToLower(dir) baseName = strings.ToLower(baseName) } diff --git a/pkg/fs/mime.go b/pkg/fs/mime.go index 46a114772..a63335ace 100644 --- a/pkg/fs/mime.go +++ b/pkg/fs/mime.go @@ -11,6 +11,7 @@ import ( ) const ( + // MimeTypeUnknown represents an unknown mime type. MimeTypeUnknown = "" ) diff --git a/pkg/fs/readlines.go b/pkg/fs/readlines.go index 3d7ded137..1cfc53f3f 100644 --- a/pkg/fs/readlines.go +++ b/pkg/fs/readlines.go @@ -9,7 +9,7 @@ import ( // ReadLines returns all lines in a text file as string slice. func ReadLines(fileName string) (lines []string, err error) { - file, err := os.Open(fileName) + file, err := os.Open(fileName) //nolint:gosec // caller-controlled path; intended file read if err != nil { return lines, err diff --git a/pkg/fs/symlink.go b/pkg/fs/symlink.go index 6098ba4e6..0939c504e 100644 --- a/pkg/fs/symlink.go +++ b/pkg/fs/symlink.go @@ -19,7 +19,7 @@ func SymlinksSupported(storagePath string) (bool, error) { }(linkName, targetName) // Create empty test target file. - if targetFile, err := os.OpenFile(targetName, os.O_RDONLY|os.O_CREATE, ModeFile); err != nil { + if targetFile, err := os.OpenFile(targetName, os.O_RDONLY|os.O_CREATE, ModeFile); err != nil { //nolint:gosec // targetName is validated by caller return false, err } else if err = targetFile.Close(); err != nil { return false, err diff --git a/pkg/fs/walk.go b/pkg/fs/walk.go index eaf57cce2..c247f7705 100644 --- a/pkg/fs/walk.go +++ b/pkg/fs/walk.go @@ -9,7 +9,8 @@ import ( func SkipWalk(name string, isDir, isSymlink bool, done Done, ignore *IgnoreList) (skip bool, result error) { isDone := done[name].Exists() - if isSymlink { + switch { + case isSymlink: // Check if symlink points to a directory. if link, err := os.Stat(name); err == nil && link.IsDir() { // Skip directories. @@ -22,12 +23,13 @@ func SkipWalk(name string, isDir, isSymlink bool, done Done, ignore *IgnoreList) } // Skip symlinked directories that cannot be resolved or are ignored, hidden, or already done. - if ignore.Ignore(name) || evalErr != nil || isDone || done[resolved].Exists() { + switch { + case ignore.Ignore(name) || evalErr != nil || isDone || done[resolved].Exists(): result = filepath.SkipDir - } else if FileExists(filepath.Join(resolved, PPStorageFilename)) { + case FileExists(filepath.Join(resolved, PPStorageFilename)): // Skip symlinked directories that contain a .ppstorage file. result = filepath.SkipDir - } else { + default: // Flag the symlink target as processed. done[resolved] = Found } @@ -36,7 +38,7 @@ func SkipWalk(name string, isDir, isSymlink bool, done Done, ignore *IgnoreList) skip = true result = filepath.SkipDir } - } else if isDir { + case isDir: skip = true if _ = ignore.Path(name); ignore.Ignore(name) || isDone { @@ -46,9 +48,11 @@ func SkipWalk(name string, isDir, isSymlink bool, done Done, ignore *IgnoreList) // Skip directories that contain a .ppstorage file. result = filepath.SkipDir } - } else if ignore.Ignore(name) || isDone { - // Skip files that are hidden or already done. - skip = true + default: + if ignore.Ignore(name) || isDone { + // Skip files that are hidden or already done. + skip = true + } } if skip { diff --git a/pkg/fs/write.go b/pkg/fs/write.go index c5b7c7d05..2e7e584f4 100644 --- a/pkg/fs/write.go +++ b/pkg/fs/write.go @@ -29,7 +29,7 @@ func WriteFile(fileName string, data []byte, perm os.FileMode) error { } } - file, err := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE|os.O_TRUNC, perm) + file, err := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE|os.O_TRUNC, perm) //nolint:gosec // caller-controlled path; intended write if err != nil { return err @@ -68,7 +68,7 @@ func WriteFileFromReader(fileName string, reader io.Reader) (err error) { var file *os.File - if file, err = os.OpenFile(fileName, os.O_RDWR|os.O_CREATE|os.O_TRUNC, ModeFile); err != nil { + if file, err = os.OpenFile(fileName, os.O_RDWR|os.O_CREATE|os.O_TRUNC, ModeFile); err != nil { //nolint:gosec // caller-controlled path; intended write return err } diff --git a/pkg/fs/write_test.go b/pkg/fs/write_test.go index 820173a5f..f082e56d9 100644 --- a/pkg/fs/write_test.go +++ b/pkg/fs/write_test.go @@ -129,7 +129,7 @@ func TestWriteFileFromReader(t *testing.T) { assert.NoError(t, writeErr) assert.True(t, unixTime >= time.Now().Unix()) - fileReader, readerErr := os.Open(filePath1) + fileReader, readerErr := os.Open(filePath1) //nolint:gosec // test helper reads temp file assert.NoError(t, readerErr) fileErr := WriteFileFromReader(filePath2, fileReader) @@ -172,7 +172,7 @@ func TestCacheFileFromReader(t *testing.T) { assert.NoError(t, writeErr) assert.True(t, unixTime >= time.Now().Unix()) - fileReader, readerErr := os.Open(filePath1) + fileReader, readerErr := os.Open(filePath1) //nolint:gosec // test helper reads temp file assert.NoError(t, readerErr) cacheFile, cacheErr := CacheFileFromReader(filePath2, fileReader) @@ -208,7 +208,7 @@ func TestWriteFile_Truncates(t *testing.T) { p := filepath.Join(dir, "f.txt") assert.NoError(t, os.WriteFile(p, []byte("LONGDATA"), ModeFile)) assert.NoError(t, WriteFile(p, []byte("short"), ModeFile)) - b, err := os.ReadFile(p) + b, err := os.ReadFile(p) //nolint:gosec // test helper reads temp file assert.NoError(t, err) assert.Equal(t, "short", string(b)) } diff --git a/pkg/fs/zip.go b/pkg/fs/zip.go index 9a84c307e..d6ef37fe1 100644 --- a/pkg/fs/zip.go +++ b/pkg/fs/zip.go @@ -24,7 +24,7 @@ func Zip(zipName string, files []string, compress bool) (err error) { var newZipFile *os.File - if newZipFile, err = os.Create(zipName); err != nil { + if newZipFile, err = os.Create(zipName); err != nil { //nolint:gosec // zipName provided by caller return err } @@ -46,7 +46,7 @@ func Zip(zipName string, files []string, compress bool) (err error) { // ZipFile adds a file to a zip archive, optionally with an alias and compression. func ZipFile(zipWriter *zip.Writer, fileName, fileAlias string, compress bool) (err error) { // Open file. - fileToZip, err := os.Open(fileName) + fileToZip, err := os.Open(fileName) //nolint:gosec // fileName provided by caller if err != nil { return err @@ -174,7 +174,7 @@ func unzipFileWithLimit(f *zip.File, dir string, fileSizeLimit int64) (fileName return fileName, err } - fd, err := os.OpenFile(fileName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) + fd, err := os.OpenFile(fileName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) //nolint:gosec // destination derived from safeJoin if err != nil { return fileName, err } diff --git a/pkg/fs/zip_test.go b/pkg/fs/zip_test.go index bacc26df1..50b6a612a 100644 --- a/pkg/fs/zip_test.go +++ b/pkg/fs/zip_test.go @@ -14,7 +14,7 @@ import ( func writeZip(t *testing.T, path string, entries map[string][]byte) { t.Helper() - f, err := os.Create(path) + f, err := os.Create(path) //nolint:gosec // test helper creates temp zip file if err != nil { t.Fatal(err) } @@ -62,7 +62,7 @@ func TestUnzip_SkipRulesAndLimits(t *testing.T) { // ok2 (1 byte) allowed; total limit reduces to 2; nothing else left that fits assert.ElementsMatch(t, []string{filepath.Join(outDir, "ok2.txt")}, files) // Ensure file written - b, rerr := os.ReadFile(filepath.Join(outDir, "ok2.txt")) + b, rerr := os.ReadFile(filepath.Join(outDir, "ok2.txt")) //nolint:gosec // test helper reads temp file assert.NoError(t, rerr) assert.Equal(t, []byte("x"), b) // Skipped contains at least the three excluded entries @@ -213,7 +213,7 @@ func writeZip64Stub(t *testing.T, path, name string, size uint64) { if len(filename) > math.MaxUint16 { t.Fatalf("filename too long") } - writeLE(uint16(len(filename))) + writeLE(uint16(len(filename))) //nolint:gosec // filename length checked above writeLE(localExtraLen) bw(filename) // zip64 extra @@ -239,7 +239,7 @@ func writeZip64Stub(t *testing.T, path, name string, size uint64) { if len(filename) > math.MaxUint16 { t.Fatalf("filename too long") } - writeLE(uint16(len(filename))) + writeLE(uint16(len(filename))) //nolint:gosec // filename length checked above writeLE(centralExtraLen) writeLE(uint16(0)) // comment len writeLE(uint16(0)) // disk start @@ -264,9 +264,9 @@ func writeZip64Stub(t *testing.T, path, name string, size uint64) { if centralLen > math.MaxUint32 || localLen > math.MaxUint32 { t.Fatalf("central or local length exceeds uint32") } - writeLE(uint32(centralLen)) - writeLE(uint32(localLen)) - writeLE(uint16(0)) // comment length + writeLE(uint32(centralLen)) //nolint:gosec // lengths checked above + writeLE(uint32(localLen)) //nolint:gosec + writeLE(uint16(0)) // comment length if err := os.WriteFile(path, buf, 0o600); err != nil { t.Fatal(err) diff --git a/pkg/geo/dist.go b/pkg/geo/dist.go index 2b9737463..ec1744752 100644 --- a/pkg/geo/dist.go +++ b/pkg/geo/dist.go @@ -5,9 +5,12 @@ import ( ) const ( - DistLimit float64 = 5000 + // DistLimit is the maximum distance in km considered realistic. + DistLimit float64 = 5000 + // ScopeDistLimit is the maximum distance in km for scope queries. ScopeDistLimit float64 = 50 - DefaultDist float64 = 2 + // DefaultDist is the default distance in km used when none is provided. + DefaultDist float64 = 2 ) // Deg returns the distance in decimal degrees based on the specified distance in meters and the latitude, @@ -22,11 +25,12 @@ func Deg(lat, meter float64) (dLat, dLng float64) { // Do not calculate the exact longitude distance in // degrees if the latitude is zero or out of range. - if lat == 0.0 { + switch { + case lat == 0.0: return dLat, dLat - } else if lat < -89.9 { + case lat < -89.9: lat = -89.9 - } else if lat > 89.9 { + case lat > 89.9: lat = 89.9 } diff --git a/pkg/geo/geo.go b/pkg/geo/geo.go index 75c5b79f2..18ad287fd 100644 --- a/pkg/geo/geo.go +++ b/pkg/geo/geo.go @@ -25,8 +25,12 @@ Additional information can be found in our Developer Guide: package geo const ( - AverageEarthRadiusKm = 6371.0 // Global-average earth radius in km - AverageEarthRadiusMeter = AverageEarthRadiusKm * 1000.0 // Global-average earth radius in m - WGS84EarthRadiusKm = 6378.137 // WGS84 earth radius in km - WGS84EarthRadiusMeter = WGS84EarthRadiusKm * 1000.0 // WGS84 earth radius in m + // AverageEarthRadiusKm is the global-average earth radius in km. + AverageEarthRadiusKm = 6371.0 + // AverageEarthRadiusMeter is the global-average earth radius in meters. + AverageEarthRadiusMeter = AverageEarthRadiusKm * 1000.0 + // WGS84EarthRadiusKm is the WGS84 equatorial earth radius in km. + WGS84EarthRadiusKm = 6378.137 + // WGS84EarthRadiusMeter is the WGS84 equatorial earth radius in meters. + WGS84EarthRadiusMeter = WGS84EarthRadiusKm * 1000.0 ) diff --git a/pkg/geo/latlng/round.go b/pkg/geo/latlng/round.go index 43da58347..e95824ae9 100644 --- a/pkg/geo/latlng/round.go +++ b/pkg/geo/latlng/round.go @@ -2,6 +2,7 @@ package latlng import "math" +// RoundDecimals defines the precision used when rounding coordinates. var RoundDecimals = float64(10000000) // Round rounds the given coordinate to six decimal places. diff --git a/pkg/geo/movement.go b/pkg/geo/movement.go index 70e885245..afe3c73a0 100644 --- a/pkg/geo/movement.go +++ b/pkg/geo/movement.go @@ -140,32 +140,34 @@ func (m *Movement) Realistic() bool { // AverageAltitude returns the average altitude. func (m *Movement) AverageAltitude() float64 { - if m.Start.Altitude != 0 && m.End.Altitude == 0 { + switch { + case m.Start.Altitude != 0 && m.End.Altitude == 0: return m.Start.Altitude - } else if m.Start.Altitude == 0 && m.End.Altitude != 0 { + case m.Start.Altitude == 0 && m.End.Altitude != 0: return m.End.Altitude - } else if m.Start.Altitude != 0 && m.End.Altitude != 0 { + case m.Start.Altitude != 0 && m.End.Altitude != 0: return (m.Start.Altitude + m.End.Altitude) / 2 + default: + return 0 } - - return 0 } // EstimateAccuracy returns the position estimate accuracy in meter. func (m *Movement) EstimateAccuracy(t time.Time) int { var a float64 - if !m.Realistic() { + switch { + case !m.Realistic(): a = m.Meter() / 2 - } else if t.Before(m.Start.Time) { + case t.Before(m.Start.Time): d := m.Start.Time.Sub(t).Hours() * 1000 d = math.Copysign(math.Sqrt(math.Abs(d)), d) a = m.Speed() * d - } else if t.After(m.End.Time) { + case t.After(m.End.Time): d := t.Sub(m.End.Time).Hours() * 1000 d = math.Copysign(math.Sqrt(math.Abs(d)), d) a = m.Speed() * d - } else { + default: a = m.Meter() / 20 } diff --git a/pkg/geo/position.go b/pkg/geo/position.go index 9c00f34e7..0eda6c629 100644 --- a/pkg/geo/position.go +++ b/pkg/geo/position.go @@ -6,6 +6,7 @@ import ( "time" ) +// Meter represents one meter in decimal degrees at the equator. const Meter = 0.00001 // Position represents a geo coordinate. diff --git a/pkg/geo/randomize.go b/pkg/geo/randomize.go index 26cc81999..45d80c671 100644 --- a/pkg/geo/randomize.go +++ b/pkg/geo/randomize.go @@ -1,10 +1,19 @@ package geo import ( - "math/rand/v2" + "crypto/rand" + "math/big" ) // Randomize adds a random offset to a value. func Randomize(value, diameter float64) float64 { - return value + (rand.Float64()-0.5)*diameter + // Use crypto/rand to avoid predictable offsets. + // randomFloat in [0,1) + n, err := rand.Int(rand.Reader, big.NewInt(1_000_000_000)) + if err != nil { + return value + } + + randomFloat := float64(n.Int64()) / 1_000_000_000.0 + return value + (randomFloat-0.5)*diameter } diff --git a/pkg/geo/s2/token_prefix.go b/pkg/geo/s2/token_prefix.go index 1a82526e6..c970537b6 100644 --- a/pkg/geo/s2/token_prefix.go +++ b/pkg/geo/s2/token_prefix.go @@ -4,6 +4,7 @@ import ( "strings" ) +// TokenPrefix is the optional prefix for S2 tokens. var TokenPrefix = "s2:" // NormalizeToken removes the prefix from a token and converts all characters to lower case. diff --git a/pkg/http/header/auth.go b/pkg/http/header/auth.go index 58e732439..4db6b5ed5 100644 --- a/pkg/http/header/auth.go +++ b/pkg/http/header/auth.go @@ -1,7 +1,7 @@ package header import ( - "crypto/sha1" + "crypto/sha1" //nolint:gosec // SHA1 retained for legacy cache key hashing "encoding/base64" "fmt" "net/http" @@ -13,7 +13,7 @@ import ( // Authentication header names. const ( Auth = "Authorization" // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization - XAuthToken = "X-Auth-Token" + XAuthToken = "X-Auth-Token" //nolint:gosec // header name, not a secret XSessionID = "X-Session-ID" ) @@ -98,7 +98,7 @@ func BasicAuth(c *gin.Context) (username, password, cacheKey string) { return "", "", "" } - cacheKey = fmt.Sprintf("%x", sha1.Sum([]byte(authToken))) + cacheKey = fmt.Sprintf("%x", sha1.Sum([]byte(authToken))) //nolint:gosec // cache key only return credentials[0], credentials[1], cacheKey } diff --git a/pkg/http/header/auth_test.go b/pkg/http/header/auth_test.go index 7e5b69fda..bb73e09bb 100644 --- a/pkg/http/header/auth_test.go +++ b/pkg/http/header/auth_test.go @@ -219,7 +219,7 @@ func TestAuthorization(t *testing.T) { Header: make(http.Header), } - token := "eyJhbGciOiJFZERTQSIsImtpZCI6IjEyMyJ9.eyJpc3MiOiJwb3J0YWw6dGVzdCIsImF1ZCI6Im5vZGU6YWJjIiwiZXhwIjoxNzAwMDAwMDB9.dGVzdC1zaWduYXR1cmUtYnl0ZXM" + token := "eyJhbGciOiJFZERTQSIsImtpZCI6IjEyMyJ9.eyJpc3MiOiJwb3J0YWw6dGVzdCIsImF1ZCI6Im5vZGU6YWJjIiwiZXhwIjoxNzAwMDAwMDB9.dGVzdC1zaWduYXR1cmUtYnl0ZXM" //nolint:gosec // static test token c.Request.Header.Add(Auth, "Bearer "+token) authType, authToken := Authorization(c) diff --git a/pkg/http/header/cache.go b/pkg/http/header/cache.go index 81cc343af..d2575e0f3 100644 --- a/pkg/http/header/cache.go +++ b/pkg/http/header/cache.go @@ -7,8 +7,7 @@ import ( ) const ( - // The CacheControl request and response header field contains directives (instructions) - // that control caching in browsers and shared caches (e.g. proxies, CDNs). + // CacheControl request and response header field contains directives for caching. // See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control CacheControl = "Cache-Control" diff --git a/pkg/http/header/cdn.go b/pkg/http/header/cdn.go index e5e57e2c6..0c87ed1f5 100644 --- a/pkg/http/header/cdn.go +++ b/pkg/http/header/cdn.go @@ -16,6 +16,7 @@ const ( ) var ( + // CdnMethods lists HTTP methods allowed via CDN. CdnMethods = []string{http.MethodGet, http.MethodHead, http.MethodOptions} ) @@ -36,13 +37,14 @@ func IsCdn(req *http.Request) bool { // AbortCdnRequest checks if the request should not be served through a CDN. func AbortCdnRequest(req *http.Request) bool { - if !IsCdn(req) { + switch { + case !IsCdn(req): return false - } else if req.Header.Get(XAuthToken) != "" { + case req.Header.Get(XAuthToken) != "": return true - } else if req.URL.Path == "/" { + case req.URL.Path == "/": return true + default: + return list.Excludes(CdnMethods, req.Method) } - - return list.Excludes(CdnMethods, req.Method) } diff --git a/pkg/http/header/cidr.go b/pkg/http/header/cidr.go index 148bfd9bb..e626f9977 100644 --- a/pkg/http/header/cidr.go +++ b/pkg/http/header/cidr.go @@ -1,7 +1,10 @@ package header const ( - CidrPodInternal = "10.0.0.0/8" + // CidrPodInternal covers internal pod traffic ranges. + CidrPodInternal = "10.0.0.0/8" + // CidrDockerInternal covers default Docker internal ranges. CidrDockerInternal = "172.16.0.0/12" + // CidrCalicoInternal covers Calico internal ranges. CidrCalicoInternal = "192.168.0.0/16" ) diff --git a/pkg/http/header/ip.go b/pkg/http/header/ip.go index 963d211ed..369c5f85b 100644 --- a/pkg/http/header/ip.go +++ b/pkg/http/header/ip.go @@ -10,6 +10,7 @@ import ( var IpRegExp = regexp.MustCompile(`[^a-zA-Z0-9:.]`) const ( + // IPv6Length represents the maximum length of an IPv6 address string. IPv6Length = 39 ) @@ -29,7 +30,8 @@ func IP(s, defaultIp string) string { fastOK := true for i := 0; i < len(s); i++ { b := s[i] - if !((b >= '0' && b <= '9') || (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || b == ':' || b == '.') { + isAlphaNum := (b >= '0' && b <= '9') || (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') + if !isAlphaNum && b != ':' && b != '.' { fastOK = false break } diff --git a/pkg/http/header/proto.go b/pkg/http/header/proto.go index cf99d1691..9ca1377b2 100644 --- a/pkg/http/header/proto.go +++ b/pkg/http/header/proto.go @@ -1,7 +1,10 @@ package header var ( - ProtoHttp = "http" + // ProtoHttp is the HTTP scheme. + ProtoHttp = "http" + // ProtoHttps is the HTTPS scheme. ProtoHttps = "https" - ProtoWss = "wss" + // ProtoWss is the secure WebSocket scheme. + ProtoWss = "wss" ) diff --git a/pkg/http/header/robots.go b/pkg/http/header/robots.go index 81087bca8..e93994c73 100644 --- a/pkg/http/header/robots.go +++ b/pkg/http/header/robots.go @@ -1,5 +1,6 @@ package header +// RobotsRule represents a robots.txt directive rule. type RobotsRule = string // RobotsTag controls how pages are indexed and crawled by search engines: diff --git a/pkg/http/header/values.go b/pkg/http/header/values.go index 6916da7f6..3b1ce7f94 100644 --- a/pkg/http/header/values.go +++ b/pkg/http/header/values.go @@ -1,6 +1,8 @@ package header const ( - Any = "*" + // Any wildcard value for header lists. + Any = "*" + // Deny disallows embedding/access (used in frame/permission headers). Deny = "DENY" ) diff --git a/pkg/http/header/webdav.go b/pkg/http/header/webdav.go index ae43bede9..bf06862ae 100644 --- a/pkg/http/header/webdav.go +++ b/pkg/http/header/webdav.go @@ -1,6 +1,8 @@ package header const ( + // XFavorite marks favorite status in WebDAV headers. XFavorite = "X-Favorite" - XModTime = "X-OC-MTime" + // XModTime conveys modification time in WebDAV headers. + XModTime = "X-OC-MTime" ) diff --git a/pkg/http/header/webhook.go b/pkg/http/header/webhook.go index 72750fd67..3df943c84 100644 --- a/pkg/http/header/webhook.go +++ b/pkg/http/header/webhook.go @@ -1,8 +1,12 @@ package header const ( - WebhookID string = "webhook-id" - WebhookSignature string = "webhook-signature" - WebhookTimestamp string = "webhook-timestamp" + // WebhookID is the request header containing a webhook identifier. + WebhookID string = "webhook-id" + // WebhookSignature carries the signature header. + WebhookSignature string = "webhook-signature" + // WebhookTimestamp carries the timestamp header. + WebhookTimestamp string = "webhook-timestamp" + // WebhookSecretPrefix prefixes stored webhook secrets. WebhookSecretPrefix string = "whsec_" ) diff --git a/pkg/http/safe/download.go b/pkg/http/safe/download.go index 9897b159d..0a0ef8719 100644 --- a/pkg/http/safe/download.go +++ b/pkg/http/safe/download.go @@ -153,12 +153,12 @@ func Download(destPath, rawURL string, opt *Options) error { } tmp := destPath + ".part" - f, err := os.OpenFile(tmp, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o600) + f, err := os.OpenFile(tmp, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o600) //nolint:gosec // destPath validated by caller; temp file if err != nil { return err } defer func() { - f.Close() + _ = f.Close() if err != nil { _ = os.Remove(tmp) } @@ -180,10 +180,8 @@ func Download(destPath, rawURL string, opt *Options) error { if err = f.Close(); err != nil { return err } - if err = os.Rename(tmp, destPath); err != nil { - return err - } - return nil + + return os.Rename(tmp, destPath) } func isPrivateOrDisallowedIP(ip net.IP) bool { diff --git a/pkg/http/safe/download_redirect_test.go b/pkg/http/safe/download_redirect_test.go index 2a124f351..46b1441b6 100644 --- a/pkg/http/safe/download_redirect_test.go +++ b/pkg/http/safe/download_redirect_test.go @@ -50,7 +50,7 @@ func TestDownload_AllowRedirectToPrivate(t *testing.T) { if err := Download(dest, ts.URL, &Options{Timeout: 5 * time.Second, MaxSizeBytes: 1 << 20, AllowPrivate: true}); err != nil { t.Fatalf("unexpected error: %v", err) } - b, err := os.ReadFile(dest) + b, err := os.ReadFile(dest) //nolint:gosec // test reads temp file if err != nil || string(b) != "ok" { t.Fatalf("unexpected content: %v %q", err, string(b)) } diff --git a/pkg/http/safe/download_test.go b/pkg/http/safe/download_test.go index 7e1937828..9dbf38b88 100644 --- a/pkg/http/safe/download_test.go +++ b/pkg/http/safe/download_test.go @@ -21,7 +21,7 @@ func TestSafeDownload_OK(t *testing.T) { if err := Download(dest, ts.URL, &Options{Timeout: 5 * time.Second, MaxSizeBytes: 1024, AllowPrivate: true}); err != nil { t.Fatal(err) } - b, err := os.ReadFile(dest) + b, err := os.ReadFile(dest) //nolint:gosec // test reads temp file if err != nil || string(b) != "hello" { t.Fatalf("unexpected content: %v %q", err, string(b)) } diff --git a/pkg/http/safe/options.go b/pkg/http/safe/options.go index b9108a517..c2597138c 100644 --- a/pkg/http/safe/options.go +++ b/pkg/http/safe/options.go @@ -21,9 +21,12 @@ var ( defaultTimeout = 30 * time.Second defaultMaxSize = int64(200 * 1024 * 1024) // 200 MiB + // ErrSchemeNotAllowed is returned when a URL scheme is not permitted. ErrSchemeNotAllowed = errors.New("invalid scheme (only http/https allowed)") - ErrSizeExceeded = errors.New("response exceeds maximum allowed size") - ErrPrivateIP = errors.New("connection to private or loopback address not allowed") + // ErrSizeExceeded is returned when a response exceeds the configured limit. + ErrSizeExceeded = errors.New("response exceeds maximum allowed size") + // ErrPrivateIP is returned when the target resolves to a private or loopback address. + ErrPrivateIP = errors.New("connection to private or loopback address not allowed") ) // envInt64 returns an int64 from env or -1 if unset/invalid. diff --git a/pkg/http/scheme/const.go b/pkg/http/scheme/const.go index dc23c7bdc..c3190f3c3 100644 --- a/pkg/http/scheme/const.go +++ b/pkg/http/scheme/const.go @@ -4,19 +4,31 @@ package scheme type Type = string const ( - File Type = "file" - Data Type = "data" - Base64 Type = "base64" - Http Type = "http" - Https Type = "https" - Websocket Type = "wss" - Unix Type = "unix" - HttpUnix Type = "http+unix" - Unixgram Type = "unixgram" + // File scheme. + File Type = "file" + // Data scheme. + Data Type = "data" + // Base64 scheme. + Base64 Type = "base64" + // Http scheme. + Http Type = "http" + // Https scheme. + Https Type = "https" + // Websocket scheme (secure). + Websocket Type = "wss" + // Unix scheme. + Unix Type = "unix" + // HttpUnix scheme. + HttpUnix Type = "http+unix" + // Unixgram scheme. + Unixgram Type = "unixgram" + // Unixpacket scheme. Unixpacket Type = "unixpacket" ) var ( + // HttpsData lists allowed schemes (https, data). HttpsData = []string{Https, Data} + // HttpsHttp lists allowed schemes (https, http). HttpsHttp = []string{Https, Http} ) diff --git a/pkg/i18n/i18n.go b/pkg/i18n/i18n.go index 4566b8a67..2539753de 100644 --- a/pkg/i18n/i18n.go +++ b/pkg/i18n/i18n.go @@ -34,7 +34,10 @@ import ( //go:generate xgettext --no-wrap --language=c --from-code=UTF-8 --output=../../assets/locales/messages.pot messages.go +// Message represents a localized message identifier. type Message int + +// MessageMap maps message IDs to their localized strings. type MessageMap map[Message]string var noVars []interface{} diff --git a/pkg/i18n/locales.go b/pkg/i18n/locales.go index bf0cb29f0..3ba46517c 100644 --- a/pkg/i18n/locales.go +++ b/pkg/i18n/locales.go @@ -6,30 +6,45 @@ import ( "github.com/leonelquinteros/gotext" ) +// Locale represents a language/region tag (e.g., "en", "pt_BR"). type Locale string const ( - German Locale = "de" - English Locale = "en" - Spanish Locale = "es" - French Locale = "fr" - Dutch Locale = "nl" - Polish Locale = "pl" - Portuguese Locale = "pt" + // German locale. + German Locale = "de" + // English locale. + English Locale = "en" + // Spanish locale. + Spanish Locale = "es" + // French locale. + French Locale = "fr" + // Dutch locale. + Dutch Locale = "nl" + // Polish locale. + Polish Locale = "pl" + // Portuguese locale. + Portuguese Locale = "pt" + // BrazilianPortuguese locale. BrazilianPortuguese Locale = "pt_BR" - Russian Locale = "ru" - ChineseSimplified Locale = "zh" - ChineseTraditional Locale = "zh_TW" - Default = English + // Russian locale. + Russian Locale = "ru" + // ChineseSimplified locale. + ChineseSimplified Locale = "zh" + // ChineseTraditional locale. + ChineseTraditional Locale = "zh_TW" + // Default locale used when none is supplied. + Default = English ) var localeDir = "../../assets/locales" var locale = Default +// SetDir sets the path to the locales directory. func SetDir(dir string) { localeDir = dir } +// SetLocale sets the current locale. func SetLocale(loc string) { switch len(loc) { case 2: @@ -45,6 +60,7 @@ func SetLocale(loc string) { gotext.Configure(localeDir, string(locale), "default") } +// Locale returns the string value of the locale. func (l Locale) Locale() string { return string(l) } diff --git a/pkg/i18n/messages.go b/pkg/i18n/messages.go index 0bef9d508..5aa94acea 100644 --- a/pkg/i18n/messages.go +++ b/pkg/i18n/messages.go @@ -1,7 +1,10 @@ package i18n +// Message and Error identifiers. const ( + // ErrUnexpected is returned for unexpected errors. ErrUnexpected Message = iota + 1 + // ErrBadRequest indicates malformed input. ErrBadRequest ErrSaveFailed ErrDeleteFailed @@ -99,6 +102,7 @@ const ( MsgActivated ) +// Messages holds default English message strings. var Messages = MessageMap{ // Error messages: ErrUnexpected: gettext("Something went wrong, try again"), diff --git a/pkg/i18n/response.go b/pkg/i18n/response.go index fb3139448..4868b4a03 100644 --- a/pkg/i18n/response.go +++ b/pkg/i18n/response.go @@ -2,6 +2,7 @@ package i18n import "strings" +// Response represents an i18n-aware response payload. type Response struct { Code int `json:"code"` Err string `json:"error,omitempty"` @@ -17,6 +18,7 @@ func (r Response) String() string { } } +// LowerString returns the lowercased message string. func (r Response) LowerString() string { return strings.ToLower(r.String()) } @@ -25,10 +27,12 @@ func (r Response) Error() string { return r.Err } +// Success reports whether the response code indicates success (2xx). func (r Response) Success() bool { return r.Err == "" && r.Code < 400 } +// NewResponse builds a Response with the given code, message ID, and optional parameters. func NewResponse(code int, id Message, params ...interface{}) Response { if code < 400 { return Response{Code: code, Msg: Msg(id, params...)} diff --git a/pkg/list/add.go b/pkg/list/add.go index d609d1d15..3a95ef224 100644 --- a/pkg/list/add.go +++ b/pkg/list/add.go @@ -2,13 +2,14 @@ package list // Add adds a string to the list if it does not exist yet. func Add(list []string, s string) []string { - if s == "" { + switch { + case s == "": return list - } else if len(list) == 0 { + case len(list) == 0: return []string{s} - } else if Contains(list, s) { + case Contains(list, s): return list + default: + return append(list, s) } - - return append(list, s) } diff --git a/pkg/list/attributes.go b/pkg/list/attributes.go index ecad2796e..bad6cbcbb 100644 --- a/pkg/list/attributes.go +++ b/pkg/list/attributes.go @@ -45,9 +45,8 @@ func (list Attr) Strings() []string { if s == "" { continue - } else if i == 0 { - // Skip check. - } else if result[i-1] == s { + } + if i > 0 && result[i-1] == s { continue } @@ -68,13 +67,14 @@ func (list Attr) Strings() []string { // Sort sorts the attributes by key. func (list Attr) Sort() Attr { sort.Slice(list, func(i, j int) bool { - if list[i].Key == list[j].Key { + switch { + case list[i].Key == list[j].Key: return list[i].Value < list[j].Value - } else if list[i].Key == Any { + case list[i].Key == Any: return false - } else if list[j].Key == Any { + case list[j].Key == Any: return true - } else { + default: return list[i].Key < list[j].Key } }) @@ -122,11 +122,12 @@ func (list Attr) Find(s string) (a KeyValue) { } else { for i := range list { if strings.EqualFold(attr.Key, list[i].Key) { - if attr.Value == enum.True && list[i].Value == enum.False { + switch { + case attr.Value == enum.True && list[i].Value == enum.False: return KeyValue{Key: "", Value: ""} - } else if attr.Value == list[i].Value { + case attr.Value == list[i].Value: return *list[i] - } else if list[i].Value == Any { + case list[i].Value == Any: a = *list[i] } } else if list[i].Key == Any && attr.Value != enum.False { diff --git a/pkg/list/contains.go b/pkg/list/contains.go index 8ea88685d..fe11d6594 100644 --- a/pkg/list/contains.go +++ b/pkg/list/contains.go @@ -30,12 +30,8 @@ func ContainsAny(l, s []string) bool { return false } - // If second list contains All, it's a wildcard match. - if s[0] == Any { - return true - } - for j := 1; j < len(s); j++ { - if s[j] == Any { + for _, v := range s { + if v == Any { return true } } diff --git a/pkg/list/ordered/list.go b/pkg/list/ordered/list.go index 0a1dcc652..fdf7e8faf 100644 --- a/pkg/list/ordered/list.go +++ b/pkg/list/ordered/list.go @@ -51,7 +51,7 @@ func (l *list[K, V]) Back() *Element[K, V] { return l.root.prev } -// Remove detaches e from the list while keeping the remaining neighbours +// Remove detaches e from the list while keeping the remaining neighbors // correctly linked. After removal the element's next/prev references are // zeroed so the node can be safely re-used or left for GC without retaining // other elements. diff --git a/pkg/list/ordered/map.go b/pkg/list/ordered/map.go index 79160d167..7a47b02f2 100644 --- a/pkg/list/ordered/map.go +++ b/pkg/list/ordered/map.go @@ -130,7 +130,7 @@ func (m *Map[K, V]) AllFromBack() iter.Seq2[K, V] { } // Keys returns an iterator that yields all keys in insertion order. Use -// slices.Collect(m.Keys()) if a materialised slice is required. +// slices.Collect(m.Keys()) if a materialized slice is required. func (m *Map[K, V]) Keys() iter.Seq[K] { return func(yield func(key K) bool) { for el := m.Front(); el != nil; el = el.Next() { diff --git a/pkg/list/ordered/map_test.go b/pkg/list/ordered/map_test.go index 3d7466dff..c58d1d643 100644 --- a/pkg/list/ordered/map_test.go +++ b/pkg/list/ordered/map_test.go @@ -1,5 +1,7 @@ package ordered_test +//revive:disable:var-naming // benchmark helpers follow Go benchmark naming with underscores + import ( "slices" "strconv" @@ -493,7 +495,7 @@ func benchmarkOrderedMap_Len(multiplier int) func(b *testing.B) { temp = m.Len() } - // prevent compiler from optimising Len away. + // prevent compiler from optimizing Len away. tempInt = temp } } @@ -800,7 +802,7 @@ func BenchmarkOrderedMapString_Has(b *testing.B) { } func nothing(v interface{}) { - v = false + _ = v } func benchmarkBigMap_Set() func(b *testing.B) { diff --git a/pkg/list/ordered/sync_test.go b/pkg/list/ordered/sync_test.go index 7157814ce..e20000947 100644 --- a/pkg/list/ordered/sync_test.go +++ b/pkg/list/ordered/sync_test.go @@ -2,7 +2,7 @@ package ordered import ( "fmt" - "math/rand" + "math/rand" //nolint:gosec // pseudo-random is sufficient for concurrency tests "sync" "testing" ) @@ -10,11 +10,15 @@ import ( func TestRaceCondition(t *testing.T) { m := NewSyncMap[int, int]() wg := &sync.WaitGroup{} + //nolint:gosec // pseudo-random is sufficient for race testing + randInt := func() int { + return rand.Intn(100) + } var asyncGet = func() { wg.Add(1) go func() { - key := rand.Intn(100) + key := randInt() m.Get(key) wg.Done() }() @@ -23,8 +27,8 @@ func TestRaceCondition(t *testing.T) { var asyncSet = func() { wg.Add(1) go func() { - key := rand.Intn(100) - value := rand.Intn(100) + key := randInt() + value := randInt() m.Set(key, value) wg.Done() }() @@ -33,7 +37,7 @@ func TestRaceCondition(t *testing.T) { var asyncDelete = func() { wg.Add(1) go func() { - key := rand.Intn(100) + key := randInt() m.Delete(key) wg.Done() }() @@ -42,7 +46,7 @@ func TestRaceCondition(t *testing.T) { var asyncHas = func() { wg.Add(1) go func() { - key := rand.Intn(100) + key := randInt() m.Has(key) wg.Done() }() @@ -51,8 +55,8 @@ func TestRaceCondition(t *testing.T) { var asyncReplaceKEy = func() { wg.Add(1) go func() { - key := rand.Intn(100) - newKey := rand.Intn(100) + key := randInt() + newKey := randInt() m.ReplaceKey(key, newKey) wg.Done() }() @@ -61,8 +65,8 @@ func TestRaceCondition(t *testing.T) { var asyncGetOrDefault = func() { wg.Add(1) go func() { - key := rand.Intn(100) - def := rand.Intn(100) + key := randInt() + def := randInt() m.GetOrDefault(key, def) wg.Done() }() diff --git a/pkg/log/dummy/logger.go b/pkg/log/dummy/logger.go index ce31b7076..bde7a6d5b 100644 --- a/pkg/log/dummy/logger.go +++ b/pkg/log/dummy/logger.go @@ -1,5 +1,7 @@ package dummy +//revive:disable:exported + import ( "context" "io" diff --git a/pkg/log/status/error_test.go b/pkg/log/status/error_test.go index 8f60f2464..eb6e92c0b 100644 --- a/pkg/log/status/error_test.go +++ b/pkg/log/status/error_test.go @@ -20,7 +20,7 @@ func TestError(t *testing.T) { }, { name: "SanitizeSpecialCharacters", - err: errors.New("permission denied { DROP TABLE users; }\n"), + err: errors.New("permission denied { DROP TABLE users; }"), }, { name: "WhitespaceOnly", diff --git a/pkg/media/README.md b/pkg/media/README.md index 9e9bf1665..14ffc0d5c 100644 --- a/pkg/media/README.md +++ b/pkg/media/README.md @@ -1,19 +1,20 @@ -Hybrid Photo/Video File Support -=============================== +## PhotoPrism — Media Package -## Apple iPhone and iPad +**Last Updated:** November 22, 2025 + +### Apple iPhone and iPad [iOS Live Photos](https://developer.apple.com/live-photos/) consist of a JPEG/HEIC image and a QuickTime AVC/HEVC video, which are both required for viewing. We recommend [using an app like PhotoSync](https://docs.photoprism.app/user-guide/sync/mobile-devices/#photosync) to upload Live Photos to PhotoPrism, since the iOS web upload usually only submits the HEIC image file without the video. -## Android Devices +### Android Devices Some Samsung and Google Android devices support taking "Motion Photos" with the included Camera app. Motion Photos are JPEG/HEIC image with a short MP4 video embedded after the image data. The image part of these files can be opened in any image viewer that supports JPEG/HEIC, but the video part cannot. However, since the MP4 video is simply appended at the end of the image file, it can be easily read by our software and streamed through the API as needed. -## Introductory Tutorials +### Introductory Tutorials | Title | Date | URL | |---------------------------------------------------------|----------|------------------------------------------------------------------------------------| @@ -24,7 +25,7 @@ The image part of these files can be opened in any image viewer that supports JP | Working with Motion Photos | Jan 2019 | https://medium.com/android-news/working-with-motion-photos-da0aa49b50c | | Google: Behind the Motion Photos Technology in Pixel 2 | Mar 2018 | https://blog.research.google/2018/03/behind-motion-photos-technology-in.html | -## Software Libraries and References +### Software Libraries and References | Title | URL | |------------------------------------------------------|-------------------------------------------------------------------------| @@ -37,19 +38,15 @@ The image part of these files can be opened in any image viewer that supports JP | How to use the io.Reader interface | https://yourbasic.org/golang/io-reader-interface-explained/ | | AV1 Codec ISO Media File Format | https://aomediacodec.github.io/av1-isobmff | -## Related GitHub Issues +### Related GitHub Issues - https://github.com/photoprism/photoprism/issues/439 (Samsung: Initial support for Motion Photos) - https://github.com/photoprism/photoprism/issues/1739 (Google: Initial support for Motion Photos) - https://github.com/photoprism/photoprism/issues/2788 (Metadata: Flag Samsung/Google Motion Photos as Live Photos) - https://github.com/cliveontoast/GoMoPho/issues/23 (Google Motion Photos Video Extractor: Add Android 12 Support) -## Related Pull Requests +### Related Pull Requests - https://github.com/photoprism/photoprism/pull/3709 (Google: Initial support for Motion Photos) - https://github.com/photoprism/photoprism/pull/3722 (Google: Add support for Motion Photos) - https://github.com/photoprism/photoprism/pull/3660 (Samsung: Improved support for Motion Photos) - ----- - -*PhotoPrism® is a [registered trademark](https://www.photoprism.app/trademark). By using the software and services we provide, you agree to our [Terms of Service](https://www.photoprism.app/terms), [Privacy Policy](https://www.photoprism.app/privacy), and [Code of Conduct](https://www.photoprism.app/code-of-conduct). Docs are [available](https://link.photoprism.app/github-docs) under the [CC BY-NC-SA 4.0 License](https://creativecommons.org/licenses/by-nc-sa/4.0/); [additional terms](https://github.com/photoprism/photoprism/blob/develop/assets/README.md) may apply.* diff --git a/pkg/media/colors/chroma.go b/pkg/media/colors/chroma.go index 9d6b0827b..98af30a71 100644 --- a/pkg/media/colors/chroma.go +++ b/pkg/media/colors/chroma.go @@ -23,7 +23,7 @@ func (c Chroma) Hex() string { // Uint returns the colourfulness in percent as unsigned integer. func (c Chroma) Uint() uint { - return uint(c.Percent()) + return uint(c.Percent()) //nolint:gosec // Percent is bounded 0..100 } // Int returns the colourfulness in percent as integer. diff --git a/pkg/media/colors/colors.go b/pkg/media/colors/colors.go index 9b509ef99..bef2456dd 100644 --- a/pkg/media/colors/colors.go +++ b/pkg/media/colors/colors.go @@ -30,28 +30,48 @@ import ( "github.com/photoprism/photoprism/pkg/txt" ) +// Color represents a indexed color value. type Color int16 + +// Colors is a slice of Color values. type Colors []Color const ( + // Black color. Black Color = iota + // Grey color. Grey + // Brown color. Brown + // Gold color. Gold + // White color. White + // Purple color. Purple + // Blue color. Blue + // Cyan color. Cyan + // Teal color. Teal + // Green color. Green + // Lime color. Lime + // Yellow color. Yellow + // Magenta color. Magenta + // Orange color. Orange + // Red color. Red + // Pink color. Pink ) +// All lists all defined colors in display order. var All = Colors{ Purple, Magenta, @@ -71,6 +91,7 @@ var All = Colors{ Black, } +// Names maps Color to their lowercase names. var Names = map[Color]string{ Black: "black", // 0 Grey: "grey", // 1 @@ -90,6 +111,7 @@ var Names = map[Color]string{ Pink: "pink", // F } +// Weights assigns relative importance to colors. var Weights = map[Color]uint16{ Grey: 1, Black: 2, @@ -109,14 +131,17 @@ var Weights = map[Color]uint16{ Magenta: 5, } +// Name returns the lowercase name for the color. func (c Color) Name() string { return Names[c] } +// ID returns the numeric identifier for the color. func (c Color) ID() int16 { return int16(c) } +// Hex returns the hex nibble for the color or "0" if out of range. func (c Color) Hex() string { if c < 0 || c > 15 { return "0" @@ -125,6 +150,7 @@ func (c Color) Hex() string { return fmt.Sprintf("%X", c) } +// Hex returns the concatenated hex values for the slice. func (c Colors) Hex() (result string) { for _, indexedColor := range c { result += indexedColor.Hex() @@ -133,6 +159,7 @@ func (c Colors) Hex() (result string) { return result } +// List returns a slice of maps with slug, display name, and example color. func (c Colors) List() []map[string]string { result := make([]map[string]string, 0, len(c)) diff --git a/pkg/media/colors/examples.go b/pkg/media/colors/examples.go index e7b58d2be..c663fd106 100644 --- a/pkg/media/colors/examples.go +++ b/pkg/media/colors/examples.go @@ -1,5 +1,6 @@ package colors +// ColorExamples contains representative hex values for each Color. var ColorExamples = map[Color]string{ Black: "#212121", Grey: "#9E9E9E", diff --git a/pkg/media/colors/lightmap.go b/pkg/media/colors/lightmap.go index 919f032de..63afeafc3 100644 --- a/pkg/media/colors/lightmap.go +++ b/pkg/media/colors/lightmap.go @@ -1,5 +1,6 @@ package colors +// LightMap stores luminance values for a palette. type LightMap []Luminance // Hex returns all luminance value as a hex encoded string. @@ -69,7 +70,7 @@ func (m LightMap) Diff() (result int) { result = 1 for _, val := range diffValues { - result = result << 1 + result <<= 1 a := 0 b := 0 @@ -83,7 +84,7 @@ func (m LightMap) Diff() (result int) { } if a+4 > b { - result += 1 + result++ } } diff --git a/pkg/media/colors/lightmap_test.go b/pkg/media/colors/lightmap_test.go index 836deacc1..ef8aa92cb 100644 --- a/pkg/media/colors/lightmap_test.go +++ b/pkg/media/colors/lightmap_test.go @@ -63,16 +63,16 @@ func TestLightMap_Diff(t *testing.T) { t.Run("Happy", func(t *testing.T) { m1 := LightMap{8, 13, 7, 2, 2, 3, 6, 3, 4} d1 := m1.Diff() - t.Log(strconv.FormatUint(uint64(d1), 2)) + t.Log(strconv.FormatUint(uint64(uint16(d1)), 2)) //nolint:gosec // test logging m2 := LightMap{8, 13, 7, 3, 1, 3, 5, 3, 4} d2 := m2.Diff() - t.Log(strconv.FormatUint(uint64(d2), 2)) + t.Log(strconv.FormatUint(uint64(uint16(d2)), 2)) //nolint:gosec // test logging m3 := LightMap{9, 13, 7, 8, 2, 4, 5, 3, 4} d3 := m3.Diff() - t.Log(strconv.FormatUint(uint64(d3), 2)) + t.Log(strconv.FormatUint(uint64(uint16(d3)), 2)) //nolint:gosec // test logging m4 := LightMap{9, 13, 7, 7, 2, 4, 6, 2, 3} d4 := m4.Diff() - t.Log(strconv.FormatUint(uint64(d4), 2)) + t.Log(strconv.FormatUint(uint64(uint16(d4)), 2)) //nolint:gosec // test logging t.Logf("values: %d, %d, %d, %d", d1, d2, d3, d4) }) diff --git a/pkg/media/colors/luminance.go b/pkg/media/colors/luminance.go index cb8190b91..8c25150fd 100644 --- a/pkg/media/colors/luminance.go +++ b/pkg/media/colors/luminance.go @@ -2,8 +2,10 @@ package colors import "fmt" +// Luminance represents a luminance value. type Luminance int16 +// Hex returns the hex string for the luminance value. func (l Luminance) Hex() string { return fmt.Sprintf("%X", l) } diff --git a/pkg/media/colors/map.go b/pkg/media/colors/map.go index 0bb4f2122..06a4a3808 100644 --- a/pkg/media/colors/map.go +++ b/pkg/media/colors/map.go @@ -2,6 +2,7 @@ package colors import "image/color" +// ColorMap maps RGBA values to Color enums. var ColorMap = map[color.RGBA]Color{ {0x00, 0x00, 0x00, 0xff}: Black, {0xe0, 0xe0, 0xe0, 0xff}: Grey, diff --git a/pkg/media/colors/profiles.go b/pkg/media/colors/profiles.go index 9363ba594..f75fd9311 100644 --- a/pkg/media/colors/profiles.go +++ b/pkg/media/colors/profiles.go @@ -2,6 +2,7 @@ package colors import "strings" +// Profile represents a color profile name. type Profile string // Supported color profiles. diff --git a/pkg/media/colors/srgb.go b/pkg/media/colors/srgb.go index 41be5eb29..ef57eef09 100644 --- a/pkg/media/colors/srgb.go +++ b/pkg/media/colors/srgb.go @@ -2,7 +2,8 @@ package colors import ( "image" - _ "image/jpeg" + _ "image/jpeg" // register JPEG + _ "image/png" // register PNG (may appear in decoded sources) "runtime" "github.com/mandykoh/prism" diff --git a/pkg/media/colors/srgb_test.go b/pkg/media/colors/srgb_test.go index 09e898e54..c32f8b754 100644 --- a/pkg/media/colors/srgb_test.go +++ b/pkg/media/colors/srgb_test.go @@ -11,7 +11,7 @@ import ( ) func writeImage(path string, img image.Image) error { - imgFile, err := os.Create(path) + imgFile, err := os.Create(path) //nolint:gosec // test temp file if err != nil { return err @@ -32,7 +32,7 @@ func TestToSRGB(t *testing.T) { t.Logf("testfile: %s", testFile) - imgFile, err := os.Open(testFile) + imgFile, err := os.Open(testFile) //nolint:gosec // test temp file if err != nil { t.Fatal(err) diff --git a/pkg/media/data_url.go b/pkg/media/data_url.go index 2eb83bcd6..fc38d1fcd 100644 --- a/pkg/media/data_url.go +++ b/pkg/media/data_url.go @@ -82,7 +82,7 @@ func ReadUrl(fileUrl string, schemes []string) (data []byte, err error) { // Fetch the file data from the specified URL, depending on its scheme. switch u.Scheme { case scheme.Https, scheme.Http, scheme.Unix, scheme.HttpUnix: - resp, httpErr := http.Get(fileUrl) + resp, httpErr := http.Get(fileUrl) //nolint:gosec // URL already validated by caller; https/http only if httpErr != nil { return data, fmt.Errorf("invalid %s url (%s)", u.Scheme, httpErr) @@ -100,7 +100,7 @@ func ReadUrl(fileUrl string, schemes []string) (data []byte, err error) { return DecodeBase64String(binaryData) } case scheme.File: - if data, err = os.ReadFile(fileUrl); err != nil { + if data, err = os.ReadFile(fileUrl); err != nil { //nolint:gosec // fileUrl validated earlier return data, fmt.Errorf("invalid %s url (%s)", u.Scheme, err) } default: diff --git a/pkg/media/data_url_test.go b/pkg/media/data_url_test.go index e1e63ff65..5c4b9cea0 100644 --- a/pkg/media/data_url_test.go +++ b/pkg/media/data_url_test.go @@ -117,7 +117,8 @@ func TestDataUrl_WebpDetection(t *testing.T) { // Minimal RIFF/WEBP container header // RIFF WEBP VP8 + padding riff := []byte{'R', 'I', 'F', 'F', 26, 0, 0, 0, 'W', 'E', 'B', 'P', 'V', 'P', '8', ' '} - buf := append(riff, bytes.Repeat([]byte{0}, 32)...) + riff = append(riff, bytes.Repeat([]byte{0}, 32)...) + buf := riff s := DataUrl(bytes.NewReader(buf)) assert.True(t, strings.HasPrefix(s, "data:image/webp;base64,")) } diff --git a/pkg/media/orientation.go b/pkg/media/orientation.go index a00b20064..a8417ac09 100644 --- a/pkg/media/orientation.go +++ b/pkg/media/orientation.go @@ -7,7 +7,9 @@ import "strings" type Orientation = string const ( - KeepOrientation Orientation = "keep" + // KeepOrientation preserves existing orientation metadata. + KeepOrientation Orientation = "keep" + // ResetOrientation strips orientation metadata. ResetOrientation Orientation = "reset" ) diff --git a/pkg/media/preview.go b/pkg/media/preview.go index 596779c56..d59e3457a 100644 --- a/pkg/media/preview.go +++ b/pkg/media/preview.go @@ -8,5 +8,8 @@ import ( "github.com/photoprism/photoprism/pkg/fs" ) +// PreviewFileTypes lists MIME types eligible for preview generation. var PreviewFileTypes = []string{fs.ImageJpeg.String(), fs.ImagePng.String()} + +// PreviewExpr is a SQL expression containing allowed preview MIME types. var PreviewExpr = gorm.Expr("'" + strings.Join(PreviewFileTypes, "','") + "'") diff --git a/pkg/media/projection/types.go b/pkg/media/projection/types.go index c628ac206..7ccf0f3dd 100644 --- a/pkg/media/projection/types.go +++ b/pkg/media/projection/types.go @@ -1,13 +1,20 @@ package projection const ( - Unknown Type = "" - Equirectangular Type = "equirectangular" - Cubestrip Type = "cubestrip" - Cylindrical Type = "cylindrical" - TransverseCylindrical Type = "transverse-cylindrical" + // Unknown projection. + Unknown Type = "" + // Equirectangular projection type. + Equirectangular Type = "equirectangular" + // Cubestrip projection type. + Cubestrip Type = "cubestrip" + // Cylindrical projection type. + Cylindrical Type = "cylindrical" + // TransverseCylindrical projection type. + TransverseCylindrical Type = "transverse-cylindrical" + // PseudocylindricalCompromise projection type. PseudocylindricalCompromise Type = "pseudocylindrical-compromise" - Other Type = "other" + // Other projection type. + Other Type = "other" ) // Types maps identifiers to known types. diff --git a/pkg/media/source.go b/pkg/media/source.go index 9ca92f532..97107554c 100644 --- a/pkg/media/source.go +++ b/pkg/media/source.go @@ -1,9 +1,12 @@ package media +// Src identifies a media source. type Src = string // Data source types. const ( - SrcLocal Src = "local" + // SrcLocal indicates the media originates from local storage. + SrcLocal Src = "local" + // SrcRemote indicates the media originates from a remote source. SrcRemote Src = "remote" ) diff --git a/pkg/media/video/brands.go b/pkg/media/video/brands.go index 92e9b6a81..b08aa77b2 100644 --- a/pkg/media/video/brands.go +++ b/pkg/media/video/brands.go @@ -91,7 +91,7 @@ func FileTypeOffset(fileName string, brands Chunks) (int, error) { return -1, errors.New("file not found") } - file, err := os.Open(fileName) + file, err := os.Open(fileName) //nolint:gosec // fileName validated by caller if err != nil { return -1, err diff --git a/pkg/media/video/chunk.go b/pkg/media/video/chunk.go index 396806d1a..98c35f9bf 100644 --- a/pkg/media/video/chunk.go +++ b/pkg/media/video/chunk.go @@ -52,7 +52,7 @@ func (c Chunk) FileOffset(fileName string) (int, error) { return -1, errors.New("file not found") } - file, err := os.Open(fileName) + file, err := os.Open(fileName) //nolint:gosec // fileName validated by caller if err != nil { return -1, err diff --git a/pkg/media/video/codec_profile.go b/pkg/media/video/codec_profile.go index 93cc2b37f..8a1266f02 100644 --- a/pkg/media/video/codec_profile.go +++ b/pkg/media/video/codec_profile.go @@ -5,9 +5,12 @@ package video type Profile = string const ( + // ProfileBaseline indicates H.264 Baseline profile. ProfileBaseline Profile = "Baseline" - ProfileMain Profile = "Main" - ProfileHigh Profile = "High" + // ProfileMain indicates H.264 Main profile. + ProfileMain Profile = "Main" + // ProfileHigh indicates H.264 High profile. + ProfileHigh Profile = "High" ) // CodecProfile represents a codec subtype with its standardized ID, diff --git a/pkg/media/video/codecs.go b/pkg/media/video/codecs.go index 17d853d27..e15791a90 100644 --- a/pkg/media/video/codecs.go +++ b/pkg/media/video/codecs.go @@ -1,5 +1,6 @@ package video +// Codec represents a video codec identifier. type Codec = string // Standard video Codec types, see: diff --git a/pkg/media/video/probe.go b/pkg/media/video/probe.go index 5ccc4e7c7..3a36c2878 100644 --- a/pkg/media/video/probe.go +++ b/pkg/media/video/probe.go @@ -31,7 +31,7 @@ func ProbeFile(fileName string) (info Info, err error) { } // Open the file for reading. - if file, err = os.Open(fileName); err != nil { + if file, err = os.Open(fileName); err != nil { //nolint:gosec // fileName validated by caller return info, err } @@ -113,8 +113,7 @@ func Probe(file io.ReadSeeker) (info Info, err error) { } // Check major brand. - switch video.MajorBrand { - case ChunkQT.Get(): + if video.MajorBrand == ChunkQT.Get() { info.VideoType = Mov info.VideoMimeType = header.ContentTypeMov if info.MediaType == media.Video { @@ -132,8 +131,7 @@ func Probe(file io.ReadSeeker) (info Info, err error) { info.Encrypted = track.Encrypted } - switch track.Codec { - case mp4.CodecAVC1: + if track.Codec == mp4.CodecAVC1 { info.VideoCodec = CodecAvc1 } diff --git a/pkg/media/video/reader.go b/pkg/media/video/reader.go index 8212e731c..d65d9d59a 100644 --- a/pkg/media/video/reader.go +++ b/pkg/media/video/reader.go @@ -23,7 +23,7 @@ func NewReader(fileName string, offset int64) (*Reader, error) { } // Open file for reading. - file, err := os.Open(fileName) + file, err := os.Open(fileName) //nolint:gosec // fileName validated by caller if err != nil { return nil, err diff --git a/pkg/react/reactions.go b/pkg/react/reactions.go index 51bfecae1..167ff7a55 100644 --- a/pkg/react/reactions.go +++ b/pkg/react/reactions.go @@ -1,21 +1,36 @@ package react var ( - Love Emoji = "❤️" - Like Emoji = "👍" - CatLove Emoji = "😻" - LoveIt Emoji = "😍" - InLove Emoji = "🥰" - Heart Emoji = Love - Cheers Emoji = "🥂" - Hot Emoji = "🔥" - Party Emoji = "🎉" - Birthday Emoji = "🎂️" - Sparkles Emoji = "✨" - Rainbow Emoji = "🌈" - Pride Emoji = "🏳️‍🌈" + // Love reaction emoji. + Love Emoji = "❤️" + // Like reaction emoji. + Like Emoji = "👍" + // CatLove reaction emoji. + CatLove Emoji = "😻" + // LoveIt reaction emoji. + LoveIt Emoji = "😍" + // InLove reaction emoji. + InLove Emoji = "🥰" + // Heart reaction emoji (alias of Love). + Heart = Love + // Cheers reaction emoji. + Cheers Emoji = "🥂" + // Hot reaction emoji. + Hot Emoji = "🔥" + // Party reaction emoji. + Party Emoji = "🎉" + // Birthday reaction emoji. + Birthday Emoji = "🎂️" + // Sparkles reaction emoji. + Sparkles Emoji = "✨" + // Rainbow reaction emoji. + Rainbow Emoji = "🌈" + // Pride reaction emoji. + Pride Emoji = "🏳️‍🌈" + // SeeNoEvil reaction emoji. SeeNoEvil Emoji = "🙈" - Unknown Emoji = "" + // Unknown reaction fallback. + Unknown Emoji ) // Reactions specifies reaction emojis by name. diff --git a/pkg/time/tz/offset.go b/pkg/time/tz/offset.go index 6da4ebb41..051fd9a5a 100644 --- a/pkg/time/tz/offset.go +++ b/pkg/time/tz/offset.go @@ -139,7 +139,8 @@ func NormalizeUtcOffset(s string) string { func UtcOffset(utc, local time.Time, offset string) string { if offset = NormalizeUtcOffset(offset); offset != "" { return offset - } else if utc.IsZero() || local == utc { + } + if utc.IsZero() || local.Equal(utc) { return "" } diff --git a/pkg/time/tz/time.go b/pkg/time/tz/time.go index df46b39e9..44597cca7 100644 --- a/pkg/time/tz/time.go +++ b/pkg/time/tz/time.go @@ -5,6 +5,8 @@ import ( ) var ( - TimeUTC = time.UTC + // TimeUTC provides the UTC location. + TimeUTC = time.UTC + // TimeLocal provides the configured local location. TimeLocal = time.FixedZone(Local, 0) ) diff --git a/pkg/vector/alg/common.go b/pkg/vector/alg/common.go index 3bf0130e6..575463161 100644 --- a/pkg/vector/alg/common.go +++ b/pkg/vector/alg/common.go @@ -2,11 +2,13 @@ package alg import ( "container/heap" - "math/rand/v2" + "crypto/rand" + "math" + "math/big" "sync" ) -// struct denoting start and end indices of database portion to be scanned for nearest neighbours by workers in DBSCAN and OPTICS +// struct denoting start and end indices of database portion to be scanned for nearest neighbors by workers in DBSCAN and OPTICS type rangeJob struct { a, b int } @@ -103,5 +105,10 @@ func bounds(data [][]float64) []*[2]float64 { } func uniform(data *[2]float64) float64 { - return rand.Float64()*(data[1]-data[0]) + data[0] + n, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt64)) + if err != nil { + return data[0] + } + r := float64(n.Int64()) / float64(math.MaxInt64) + return r*(data[1]-data[0]) + data[0] } diff --git a/pkg/vector/alg/csv_importer.go b/pkg/vector/alg/csv_importer.go index 12f09c409..231e69ec7 100644 --- a/pkg/vector/alg/csv_importer.go +++ b/pkg/vector/alg/csv_importer.go @@ -8,9 +8,9 @@ import ( "strconv" ) -type csvImporter struct { -} +type csvImporter struct{} +// CsvImporter returns an Importer that reads vectors from CSV files. func CsvImporter() Importer { return &csvImporter{} } @@ -20,7 +20,7 @@ func (i *csvImporter) Import(file string, start, end int) ([][]float64, error) { return [][]float64{}, errInvalidRange } - f, err := os.Open(file) + f, err := os.Open(file) //nolint:gosec // caller controls path if err != nil { return [][]float64{}, err } diff --git a/pkg/vector/alg/dbscan.go b/pkg/vector/alg/dbscan.go index 0995a07aa..3fe1d6d57 100644 --- a/pkg/vector/alg/dbscan.go +++ b/pkg/vector/alg/dbscan.go @@ -1,3 +1,4 @@ +//nolint:revive,staticcheck,gocritic,gosec // clustering algorithms keep legacy style for clarity package alg import ( @@ -16,7 +17,7 @@ type dbscanClusterer struct { a []int b []int - // variables used for concurrent computation of nearest neighbours + // variables used for concurrent computation of nearest neighbors // dataset len l int // worker number @@ -38,8 +39,8 @@ type dbscanClusterer struct { d [][]float64 } -// Implementation of DBSCAN algorithm with concurrent nearest neighbour computation. The number of goroutines acting concurrently -// is controlled via workers argument. Passing 0 will result in this number being chosen arbitrarily. +// DBSCAN implements density-based clustering with concurrent nearest neighbor computation. +// The number of goroutines is controlled via workers (0 picks a default). func DBSCAN(minpts int, eps float64, workers int, distance DistFunc) (HardClusterer, error) { if minpts < 1 { return nil, errZeroMinpts @@ -195,7 +196,7 @@ func (c *dbscanClusterer) run() { } /* Divide work among c.s workers, where c.s is determined - * by the size of the data. This is based on an assumption that neighbour points of p + * by the size of the data. This is based on an assumption that neighbor points of p * are located in relatively small subsection of the input data, so the dataset can be scanned * concurrently without blocking a big number of goroutines trying to write to r */ func (c *dbscanClusterer) nearest(p int, l *int, r *[]int) { @@ -262,13 +263,14 @@ func (c *dbscanClusterer) nearestWorker() { func (c *dbscanClusterer) numWorkers() int { var b int - if c.l < 1000 { + switch { + case c.l < 1000: b = 1 - } else if c.l < 10000 { + case c.l < 10000: b = 10 - } else if c.l < 100000 { + case c.l < 100000: b = 100 - } else { + default: b = 1000 } diff --git a/pkg/vector/alg/errors.go b/pkg/vector/alg/errors.go index 188159c97..f6c7f2e60 100644 --- a/pkg/vector/alg/errors.go +++ b/pkg/vector/alg/errors.go @@ -3,13 +3,12 @@ package alg import "errors" var ( - errEmptySet = errors.New("Empty training set") - errNotTrained = errors.New("You need to train the algorithm first") - errZeroIterations = errors.New("Number of iterations cannot be less than 1") - errOneCluster = errors.New("Number of clusters cannot be less than 2") - errZeroEpsilon = errors.New("Epsilon cannot be 0") - errZeroMinpts = errors.New("MinPts cannot be 0") - errZeroWorkers = errors.New("Number of workers cannot be less than 0") - errZeroXi = errors.New("Xi cannot be 0") - errInvalidRange = errors.New("Range is invalid") + errEmptySet = errors.New("empty training set") + errZeroIterations = errors.New("number of iterations cannot be less than 1") + errOneCluster = errors.New("number of clusters cannot be less than 2") + errZeroEpsilon = errors.New("epsilon cannot be 0") + errZeroMinpts = errors.New("minpts cannot be 0") + errZeroWorkers = errors.New("number of workers cannot be less than 0") + errZeroXi = errors.New("xi cannot be 0") + errInvalidRange = errors.New("range is invalid") ) diff --git a/pkg/vector/alg/json_importer.go b/pkg/vector/alg/json_importer.go index a4572c674..189f4cac7 100644 --- a/pkg/vector/alg/json_importer.go +++ b/pkg/vector/alg/json_importer.go @@ -5,9 +5,9 @@ import ( "os" ) -type jsonImporter struct { -} +type jsonImporter struct{} +// JsonImporter returns an Importer that reads vectors from JSON files. func JsonImporter() Importer { return &jsonImporter{} } @@ -17,7 +17,7 @@ func (i *jsonImporter) Import(file string, start, end int) ([][]float64, error) return [][]float64{}, errInvalidRange } - f, err := os.ReadFile(file) + f, err := os.ReadFile(file) //nolint:gosec // caller controls path if err != nil { return [][]float64{}, err } diff --git a/pkg/vector/alg/kmeans.go b/pkg/vector/alg/kmeans.go index 74f4eff23..e94c435a4 100644 --- a/pkg/vector/alg/kmeans.go +++ b/pkg/vector/alg/kmeans.go @@ -1,3 +1,4 @@ +//nolint:revive,staticcheck,gocritic,gosec // clustering algorithms keep legacy style and use math/rand intentionally package alg import ( @@ -35,7 +36,7 @@ type kmeansClusterer struct { d [][]float64 } -// Implementation of k-means++ algorithm with online learning +// KMeans implements the k-means++ clustering algorithm with online learning support. func KMeans(iterations, clusters int, distance DistFunc) (HardClusterer, error) { if iterations < 1 { return nil, errZeroIterations @@ -122,11 +123,9 @@ func (c *kmeansClusterer) Guesses() []int { } func (c *kmeansClusterer) Predict(p []float64) int { - var ( - l int - d float64 - m float64 = c.distance(p, c.m[0]) - ) + l := 0 + m := c.distance(p, c.m[0]) + var d float64 for i := 1; i < c.number; i++ { if d = c.distance(p, c.m[i]); d < m { @@ -141,11 +140,9 @@ func (c *kmeansClusterer) Predict(p []float64) int { func (c *kmeansClusterer) Online(observations chan []float64, done chan struct{}) chan *HCEvent { c.mu.Lock() - var ( - r chan *HCEvent = make(chan *HCEvent) - l, f int = len(c.m), len(c.m[0]) - h float64 = 1 - c.alpha - ) + r := make(chan *HCEvent) + l, f := len(c.m), len(c.m[0]) + h := 1 - c.alpha c.b = make([]int, c.number) @@ -160,7 +157,7 @@ func (c *kmeansClusterer) Online(observations chan []float64, done chan struct{} var ( k int n float64 - m float64 = math.Pow(c.distance(o, c.m[0]), 2) + m = math.Pow(c.distance(o, c.m[0]), 2) ) for i := 1; i < l; i++ { @@ -226,7 +223,7 @@ func (c *kmeansClusterer) initializeMeansWithData() { d []float64 = make([]float64, len(c.d)) ) - c.m[0] = c.d[rand.IntN(len(c.d)-1)] + c.m[0] = c.d[rand.IntN(len(c.d)-1)] //nolint:gosec // pseudo-random seeding is acceptable for clustering for i := 1; i < c.number; i++ { s = 0 @@ -240,11 +237,11 @@ func (c *kmeansClusterer) initializeMeansWithData() { } } - d[j] = math.Pow(l, 2) + d[j] = l * l s += d[j] } - t = rand.Float64() * s + t = rand.Float64() * s //nolint:gosec // pseudo-random weighting is acceptable for clustering k = 0 for s = d[0]; s < t; s += d[k] { k++ diff --git a/pkg/vector/alg/kmeans_estimator.go b/pkg/vector/alg/kmeans_estimator.go index d544f0be8..14c3723cd 100644 --- a/pkg/vector/alg/kmeans_estimator.go +++ b/pkg/vector/alg/kmeans_estimator.go @@ -1,3 +1,4 @@ +//nolint:revive,staticcheck,gocritic,gosec // estimator retains legacy style and pseudo-random seeding package alg import ( diff --git a/pkg/vector/alg/optics.go b/pkg/vector/alg/optics.go index 5d4e7e7ae..9016cecc5 100644 --- a/pkg/vector/alg/optics.go +++ b/pkg/vector/alg/optics.go @@ -1,3 +1,4 @@ +//nolint:revive,staticcheck,gocritic,gosec // legacy optics implementation kept as-is for correctness package alg import ( @@ -24,7 +25,7 @@ type opticsClusterer struct { mu sync.RWMutex a, b []int - // variables used for concurrent computation of nearest neighbours and producing final mapping + // variables used for concurrent computation of nearest neighbors and producing final mapping l, s, o, f, g int j chan *rangeJob c chan *clusterJob @@ -46,7 +47,7 @@ type opticsClusterer struct { d [][]float64 } -// Implementation of OPTICS algorithm with concurrent nearest neighbour computation. The number of goroutines acting concurrently +// OPTICS implements clustering with concurrent nearest neighbor computation. The number of goroutines acting concurrently // is controlled via workers argument. Passing 0 will result in this number being chosen arbitrarily. func OPTICS(minpts int, eps, xi float64, workers int, distance DistFunc) (HardClusterer, error) { if minpts < 1 { @@ -265,7 +266,8 @@ func (c *opticsClusterer) extract() { for i < len(c.so)-1 { mib = math.Max(mib, c.re[c.so[i]].p) - if c.isSteepDown(i, &e) { + switch { + case c.isSteepDown(i, &e): as := areas[:0] for j := 0; j < len(areas); j++ { if c.re[c.so[areas[j].start]].p*c.x < mib { @@ -287,7 +289,7 @@ func (c *opticsClusterer) extract() { i = e + 1 mib = c.re[c.so[i]].p - } else if c.isSteepUp(i, &e) { + case c.isSteepUp(i, &e): ue = e + 1 as := areas[:0] @@ -311,10 +313,11 @@ func (c *opticsClusterer) extract() { d = (c.re[c.so[areas[j].start]].p - c.re[c.so[ue]].p) / c.re[c.so[areas[j].start]].p - if math.Abs(d) <= c.xi { + switch { + case math.Abs(d) <= c.xi: cs = areas[j].start ce = ue - } else if d > c.xi { + case d > c.xi: for k := areas[j].end; k > areas[j].end; k-- { if math.Abs((c.re[c.so[k]].p-c.re[c.so[ue]].p)/c.re[c.so[k]].p) <= c.xi { cs = k @@ -322,7 +325,7 @@ func (c *opticsClusterer) extract() { } } ce = ue - } else { + default: cs = areas[j].start for k := i; k < e; k++ { if math.Abs((c.re[c.so[k]].p-c.re[c.so[i]].p)/c.re[c.so[k]].p) <= c.xi { @@ -360,7 +363,7 @@ func (c *opticsClusterer) extract() { i = ue mib = c.re[c.so[i]].p - } else { + default: i++ } } @@ -467,7 +470,7 @@ func (c *opticsClusterer) clusterWorker() { } /* Divide work among c.s workers, where c.s is determined - * by the size of the data. This is based on an assumption that neighbour points of p + * by the size of the data. This is based on an assumption that neighbor points of p * are located in relatively small subsection of the input data, so the dataset can be scanned * concurrently without blocking a big number of goroutines trying to write to r */ func (c *opticsClusterer) nearest(p int, l *int, r *[]int) { diff --git a/pkg/vector/const.go b/pkg/vector/const.go index aeaae8026..28db589bf 100644 --- a/pkg/vector/const.go +++ b/pkg/vector/const.go @@ -3,9 +3,11 @@ package vector import "math" const ( + // Epsilon is the smallest non-zero float used as a numerical tolerance. Epsilon = math.SmallestNonzeroFloat64 ) +// NaN returns a quiet NaN value. func NaN() float64 { return math.NaN() } diff --git a/pkg/vector/values.go b/pkg/vector/values.go index 7e59a2999..cd2643e22 100644 --- a/pkg/vector/values.go +++ b/pkg/vector/values.go @@ -32,7 +32,7 @@ func (v Vector) Sum() float64 { // calculating the weighted mean. func (v Vector) weightedSum(w Vector) (float64, error) { if len(v) != len(w) { - return Epsilon, fmt.Errorf("Length of weights unequal to vector length") + return Epsilon, fmt.Errorf("length of weights unequal to vector length") } ws := 0.0 @@ -188,8 +188,8 @@ func CosineDist(a, b Vector) float64 { for i := 0; i < len(a); i++ { sum += a[i] * b[i] - s1 += math.Pow(a[i], 2) - s2 += math.Pow(b[i], 2) + s1 += a[i] * a[i] + s2 += b[i] * b[i] } if s1 == 0 || s2 == 0 { From ed96f381b9dd3a53f3b5ddb2c6c17c3d472b62ae Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sat, 22 Nov 2025 16:23:13 +0100 Subject: [PATCH 036/195] Media: Improve ReadUrl() implementation and tests #5330 Signed-off-by: Michael Mayer --- pkg/media/data_url.go | 9 ++++++++- pkg/media/data_url_test.go | 12 ++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/pkg/media/data_url.go b/pkg/media/data_url.go index fc38d1fcd..3bf088e6e 100644 --- a/pkg/media/data_url.go +++ b/pkg/media/data_url.go @@ -100,7 +100,14 @@ func ReadUrl(fileUrl string, schemes []string) (data []byte, err error) { return DecodeBase64String(binaryData) } case scheme.File: - if data, err = os.ReadFile(fileUrl); err != nil { //nolint:gosec // fileUrl validated earlier + path := u.Path + if path == "" { + path = u.Opaque + } + if path == "" { + return data, fmt.Errorf("invalid %s url (empty path)", u.Scheme) + } + if data, err = os.ReadFile(path); err != nil { //nolint:gosec // file path validated earlier return data, fmt.Errorf("invalid %s url (%s)", u.Scheme, err) } default: diff --git a/pkg/media/data_url_test.go b/pkg/media/data_url_test.go index 5c4b9cea0..bae0a6a35 100644 --- a/pkg/media/data_url_test.go +++ b/pkg/media/data_url_test.go @@ -5,6 +5,8 @@ import ( "io" "net/http" "net/http/httptest" + "os" + "path/filepath" "strings" "testing" @@ -75,6 +77,16 @@ func TestReadUrl(t *testing.T) { _, err := ReadUrl("file:///this/does/not/exist", []string{"file"}) assert.Error(t, err) }) + t.Run("FileSchemeValidPng", func(t *testing.T) { + tmp := t.TempDir() + fn := filepath.Join(tmp, "pic.png") + payload := append([]byte{0x89, 'P', 'N', 'G', '\r', '\n', 0x1A, '\n'}, bytes.Repeat([]byte{0}, 16)...) + assert.NoError(t, os.WriteFile(fn, payload, 0o600)) + + data, err := ReadUrl("file://"+fn, []string{"file"}) + assert.NoError(t, err) + assert.Equal(t, payload, data) + }) } func TestDataUrl_LargeBinary(t *testing.T) { From 0e6328a33d8dc0f5a8f41ab9a4cf6e6e982d5780 Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sat, 22 Nov 2025 16:23:47 +0100 Subject: [PATCH 037/195] Tests: Add pkg/capture/capture_test.go #5330 Signed-off-by: Michael Mayer --- pkg/capture/capture_test.go | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 pkg/capture/capture_test.go diff --git a/pkg/capture/capture_test.go b/pkg/capture/capture_test.go new file mode 100644 index 000000000..af3a01175 --- /dev/null +++ b/pkg/capture/capture_test.go @@ -0,0 +1,22 @@ +package capture + +import ( + "fmt" + "os" + "testing" +) + +func TestOutputMergesStdoutAndStderr(t *testing.T) { + got := Output(func() { + fmt.Print("out") + fmt.Fprint(stderrWriter(), "err") // write directly to stderr + }) + if got != "outerr" { + t.Fatalf("unexpected combined output: %q", got) + } +} + +// stderrWriter returns the current process stderr; split for test clarity. +func stderrWriter() *os.File { + return os.Stderr +} From 699ad5b50c48f787ca10a5d325b22de8e88b6abc Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sat, 22 Nov 2025 17:55:26 +0100 Subject: [PATCH 038/195] CI: Apply Go linter recommendations to "internal/commands" package #5330 Signed-off-by: Michael Mayer --- internal/commands/auth_add_test.go | 8 ++++---- internal/commands/auth_jwt_inspect.go | 2 +- internal/commands/auth_list_test.go | 2 +- internal/commands/auth_reset.go | 1 + internal/commands/auth_reset_test.go | 2 +- internal/commands/clients.go | 3 ++- internal/commands/clients_mod.go | 4 +--- internal/commands/clients_mod_test.go | 10 +++++----- internal/commands/clients_remove.go | 4 +--- internal/commands/clients_remove_test.go | 8 ++++---- internal/commands/clients_reset_test.go | 2 +- internal/commands/clients_show.go | 4 +--- internal/commands/clients_show_test.go | 2 +- internal/commands/cluster_nodes_list.go | 2 +- internal/commands/cluster_nodes_rotate.go | 7 ++++--- internal/commands/cluster_register.go | 4 ++-- internal/commands/cluster_theme_pull.go | 12 ++++-------- internal/commands/commands.go | 1 + internal/commands/download.go | 9 +++++---- internal/commands/download_format_test.go | 8 ++++---- internal/commands/download_impl.go | 2 +- internal/commands/find.go | 6 +++++- internal/commands/find_test.go | 2 +- internal/commands/migrations.go | 13 ++++++++----- internal/commands/passwd.go | 4 ++-- internal/commands/show_config.go | 5 +++-- internal/commands/show_scopes.go | 2 +- internal/commands/show_sources.go | 2 +- internal/commands/status.go | 2 +- internal/commands/users.go | 1 + internal/commands/users_add.go | 6 +----- internal/commands/users_legacy_test.go | 2 +- internal/commands/users_reset.go | 1 + internal/commands/users_reset_test.go | 2 +- internal/commands/users_show_test.go | 2 +- internal/commands/users_test.go | 6 +++--- internal/commands/vision.go | 2 +- internal/commands/vision_list.go | 2 +- 38 files changed, 79 insertions(+), 78 deletions(-) diff --git a/internal/commands/auth_add_test.go b/internal/commands/auth_add_test.go index 948499aec..08174b334 100644 --- a/internal/commands/auth_add_test.go +++ b/internal/commands/auth_add_test.go @@ -23,7 +23,7 @@ func TestAuthAddCommand(t *testing.T) { output, err := RunWithTestContext(AuthAddCommand, []string{"add", "--scope=test", "--expires=5000", "--name=xyz"}) // Check command output for plausibility. - //t.Logf(output) + // t.Logf(output) assert.NoError(t, err) assert.NotContains(t, output, "App Password") assert.Contains(t, output, "Access Token") @@ -34,7 +34,7 @@ func TestAuthAddCommand(t *testing.T) { output, err := RunWithTestContext(AuthAddCommand, []string{"add", "--scope=test", "--expires=5000", "xxxxx"}) // Check command output for plausibility. - //t.Logf(output) + // t.Logf(output) assert.Error(t, err) assert.Empty(t, output) @@ -44,7 +44,7 @@ func TestAuthAddCommand(t *testing.T) { output, err := RunWithTestContext(AuthAddCommand, []string{"add", "--scope=test", "--expires=5000", "alice"}) // Check command output for plausibility. - //t.Logf(output) + // t.Logf(output) assert.Error(t, err) assert.Empty(t, output) }) @@ -53,7 +53,7 @@ func TestAuthAddCommand(t *testing.T) { output, err := RunWithTestContext(AuthAddCommand, []string{"add", "--name=test", "--expires=5000", "alice"}) // Check command output for plausibility. - //t.Logf(output) + // t.Logf(output) assert.Error(t, err) assert.Empty(t, output) }) diff --git a/internal/commands/auth_jwt_inspect.go b/internal/commands/auth_jwt_inspect.go index bafd6d768..b987e273c 100644 --- a/internal/commands/auth_jwt_inspect.go +++ b/internal/commands/auth_jwt_inspect.go @@ -128,7 +128,7 @@ func authJWTInspectAction(ctx *cli.Context) error { // readTokenInput loads the token from CLI args, file, or STDIN. func readTokenInput(ctx *cli.Context) (string, error) { if file := strings.TrimSpace(ctx.String("file")); file != "" { - data, err := os.ReadFile(file) + data, err := os.ReadFile(file) //nolint:gosec // user-supplied path is intended if err != nil { return "", cli.Exit(err, 1) } diff --git a/internal/commands/auth_list_test.go b/internal/commands/auth_list_test.go index 33b919f23..4a341b59e 100644 --- a/internal/commands/auth_list_test.go +++ b/internal/commands/auth_list_test.go @@ -36,7 +36,7 @@ func TestAuthListCommand(t *testing.T) { output, err := RunWithTestContext(AuthListCommand, []string{"ls", "--csv", "alice"}) // Check command output for plausibility. - //t.Logf(output) + // t.Logf(output) assert.NoError(t, err) assert.Contains(t, output, "Session ID;") assert.Contains(t, output, "alice;") diff --git a/internal/commands/auth_reset.go b/internal/commands/auth_reset.go index aa01fed3f..6d7fdac84 100644 --- a/internal/commands/auth_reset.go +++ b/internal/commands/auth_reset.go @@ -10,6 +10,7 @@ import ( "github.com/photoprism/photoprism/internal/entity" ) +// AuthResetDescription explains the effect of the auth reset command. const AuthResetDescription = "This command recreates the auth_sessions database table so that it is compatible with the current version. As a result, all users and clients must re-authenticate. Note that any client access tokens and app passwords that users may have created are also deleted and must be recreated." // AuthResetCommand configures the command name, flags, and action. diff --git a/internal/commands/auth_reset_test.go b/internal/commands/auth_reset_test.go index e3478dda3..e4a4c4c20 100644 --- a/internal/commands/auth_reset_test.go +++ b/internal/commands/auth_reset_test.go @@ -21,7 +21,7 @@ func TestAuthResetCommand(t *testing.T) { output, err := RunWithTestContext(AuthResetCommand, []string{"reset"}) // Check command output for plausibility. - //t.Logf(output) + // t.Logf(output) assert.NoError(t, err) assert.Empty(t, output) diff --git a/internal/commands/clients.go b/internal/commands/clients.go index 9bebe083f..6d95b86d4 100644 --- a/internal/commands/clients.go +++ b/internal/commands/clients.go @@ -23,10 +23,11 @@ const ( ClientRegenerateSecret = "set a new randomly generated client secret" ClientEnable = "enable client authentication if disabled" ClientDisable = "disable client authentication" - ClientSecretInfo = "\nPLEASE WRITE DOWN THE %s CLIENT SECRET, AS YOU WILL NOT BE ABLE TO SEE IT AGAIN:" + ClientSecretInfo = "\nPLEASE WRITE DOWN THE %s CLIENT SECRET, AS YOU WILL NOT BE ABLE TO SEE IT AGAIN:" //nolint:gosec // informational message only ) var ( + // ClientRoleUsage describes allowed client roles for CLI help. ClientRoleUsage = fmt.Sprintf("client authorization `ROLE`, e.g. %s", acl.ClientRoles.CliUsageString()) ) diff --git a/internal/commands/clients_mod.go b/internal/commands/clients_mod.go index e7e900ad8..ba9150ec0 100644 --- a/internal/commands/clients_mod.go +++ b/internal/commands/clients_mod.go @@ -35,9 +35,7 @@ func clientsModAction(ctx *cli.Context) error { } // Find client record. - var client *entity.Client - - client = entity.FindClientByUID(frm.ID()) + client := entity.FindClientByUID(frm.ID()) if client == nil { return fmt.Errorf("client %s not found", clean.Log(frm.ID())) diff --git a/internal/commands/clients_mod_test.go b/internal/commands/clients_mod_test.go index 8a07167f0..915b187a9 100644 --- a/internal/commands/clients_mod_test.go +++ b/internal/commands/clients_mod_test.go @@ -19,7 +19,7 @@ func TestClientsModCommand(t *testing.T) { output0, err := RunWithTestContext(ClientsShowCommand, []string{"show", "cs7pvt5h8rw9aaqj"}) // Check command output for plausibility. - //t.Logf(output0) + // t.Logf(output0) assert.NoError(t, err) assert.Contains(t, output0, "AuthEnabled │ true") assert.Contains(t, output0, "oauth2") @@ -28,7 +28,7 @@ func TestClientsModCommand(t *testing.T) { output, err := RunWithTestContext(ClientsModCommand, []string{"mod", "--disable", "cs7pvt5h8rw9aaqj"}) // Check command output for plausibility. - //t.Logf(output) + // t.Logf(output) assert.NoError(t, err) assert.Empty(t, output) @@ -36,7 +36,7 @@ func TestClientsModCommand(t *testing.T) { output1, err := RunWithTestContext(ClientsShowCommand, []string{"show", "cs7pvt5h8rw9aaqj"}) // Check command output for plausibility. - //t.Logf(output1) + // t.Logf(output1) assert.NoError(t, err) assert.Contains(t, output1, "AuthEnabled │ false") @@ -51,7 +51,7 @@ func TestClientsModCommand(t *testing.T) { output3, err := RunWithTestContext(ClientsShowCommand, []string{"show", "cs7pvt5h8rw9aaqj"}) // Check command output for plausibility. - //t.Logf(output3) + // t.Logf(output3) assert.NoError(t, err) assert.Contains(t, output3, "│ AuthEnabled │ true ") }) @@ -60,7 +60,7 @@ func TestClientsModCommand(t *testing.T) { output, err := RunWithTestContext(ClientsModCommand, []string{"mod", "--regenerate", "cs7pvt5h8rw9aaqj"}) // Check command output for plausibility. - //t.Logf(output) + // t.Logf(output) assert.NoError(t, err) assert.Contains(t, output, "Client Secret") }) diff --git a/internal/commands/clients_remove.go b/internal/commands/clients_remove.go index e40e7ea2f..adcf29d97 100644 --- a/internal/commands/clients_remove.go +++ b/internal/commands/clients_remove.go @@ -40,9 +40,7 @@ func clientsRemoveAction(ctx *cli.Context) error { } // Find client record. - var m *entity.Client - - m = entity.FindClientByUID(id) + m := entity.FindClientByUID(id) if m == nil { return fmt.Errorf("client %s not found", clean.Log(id)) diff --git a/internal/commands/clients_remove_test.go b/internal/commands/clients_remove_test.go index 5877d0ea6..451b8afa1 100644 --- a/internal/commands/clients_remove_test.go +++ b/internal/commands/clients_remove_test.go @@ -11,7 +11,7 @@ func TestCientsRemoveCommand(t *testing.T) { // Run command with test context. output0, err := RunWithTestContext(ClientsShowCommand, []string{"show", "cs7pvt5h8rw9aaqj"}) - //t.Logf(output0) + // t.Logf(output0) assert.NoError(t, err) assert.NotContains(t, output0, "not found") assert.Contains(t, output0, "client") @@ -20,14 +20,14 @@ func TestCientsRemoveCommand(t *testing.T) { output, err := RunWithTestContext(ClientsRemoveCommand, []string{"rm", "cs7pvt5h8rw9aaqj"}) // Check command output for plausibility. - //t.Logf(output) + // t.Logf(output) assert.NoError(t, err) assert.Empty(t, output) // Run command with test context. output2, err := RunWithTestContext(ClientsShowCommand, []string{"show", "cs7pvt5h8rw9aaqj"}) - //t.Logf(output2) + // t.Logf(output2) assert.NoError(t, err) assert.NotContains(t, output2, "not found") assert.Contains(t, output2, "client") @@ -36,7 +36,7 @@ func TestCientsRemoveCommand(t *testing.T) { // Run command with test context. output0, err := RunWithTestContext(ClientsShowCommand, []string{"show", "cs7pvt5h8rw9aaqj"}) - //t.Logf(output0) + // t.Logf(output0) assert.NoError(t, err) assert.NotContains(t, output0, "not found") assert.Contains(t, output0, "client") diff --git a/internal/commands/clients_reset_test.go b/internal/commands/clients_reset_test.go index c9f39a318..179eb0789 100644 --- a/internal/commands/clients_reset_test.go +++ b/internal/commands/clients_reset_test.go @@ -21,7 +21,7 @@ func TestClientsResetCommand(t *testing.T) { output, err := RunWithTestContext(ClientsResetCommand, []string{"reset"}) // Check command output for plausibility. - //t.Logf(output) + // t.Logf(output) assert.NoError(t, err) assert.Empty(t, output) diff --git a/internal/commands/clients_show.go b/internal/commands/clients_show.go index b4e514358..f6fc31361 100644 --- a/internal/commands/clients_show.go +++ b/internal/commands/clients_show.go @@ -31,9 +31,7 @@ func clientsShowAction(ctx *cli.Context) error { } // Find client record. - var m *entity.Client - - m = entity.FindClientByUID(id) + m := entity.FindClientByUID(id) if m == nil { return fmt.Errorf("client %s not found", clean.Log(id)) diff --git a/internal/commands/clients_show_test.go b/internal/commands/clients_show_test.go index 3c0980855..ed45861b1 100644 --- a/internal/commands/clients_show_test.go +++ b/internal/commands/clients_show_test.go @@ -12,7 +12,7 @@ func TestClientsShowCommand(t *testing.T) { output, err := RunWithTestContext(ClientsShowCommand, []string{"show", "cs5gfen1bgxz7s9i"}) // Check command output for plausibility. - //t.Logf(output) + // t.Logf(output) assert.NoError(t, err) assert.Contains(t, output, "Alice") assert.Contains(t, output, "oauth2") diff --git a/internal/commands/cluster_nodes_list.go b/internal/commands/cluster_nodes_list.go index d6d18c582..bd2b0947a 100644 --- a/internal/commands/cluster_nodes_list.go +++ b/internal/commands/cluster_nodes_list.go @@ -56,7 +56,7 @@ func clusterNodesListAction(ctx *cli.Context) error { } // Pagination identical to API defaults. - count := int(ctx.Uint("count")) + count := int(ctx.Uint("count")) //nolint:gosec // CLI flag bounded by validation if count <= 0 || count > 1000 { count = 100 } diff --git a/internal/commands/cluster_nodes_rotate.go b/internal/commands/cluster_nodes_rotate.go index 034c95419..78fc8ebff 100644 --- a/internal/commands/cluster_nodes_rotate.go +++ b/internal/commands/cluster_nodes_rotate.go @@ -216,11 +216,12 @@ func clusterNodesRotateAction(ctx *cli.Context) error { if (resp.Secrets != nil && resp.Secrets.ClientSecret != "") || resp.Database.Password != "" { fmt.Println("PLEASE WRITE DOWN THE FOLLOWING CREDENTIALS; THEY WILL NOT BE SHOWN AGAIN:") - if resp.Secrets != nil && resp.Secrets.ClientSecret != "" && resp.Database.Password != "" { + switch { + case resp.Secrets != nil && resp.Secrets.ClientSecret != "" && resp.Database.Password != "": fmt.Printf("\n%s\n", report.Credentials("Node Client Secret", resp.Secrets.ClientSecret, "DB Password", resp.Database.Password)) - } else if resp.Secrets != nil && resp.Secrets.ClientSecret != "" { + case resp.Secrets != nil && resp.Secrets.ClientSecret != "": fmt.Printf("\n%s\n", report.Credentials("Node Client Secret", resp.Secrets.ClientSecret, "", "")) - } else if resp.Database.Password != "" { + case resp.Database.Password != "": fmt.Printf("\n%s\n", report.Credentials("DB User", resp.Database.User, "DB Password", resp.Database.Password)) } if resp.Database.DSN != "" { diff --git a/internal/commands/cluster_register.go b/internal/commands/cluster_register.go index 30884ffeb..4ff345036 100644 --- a/internal/commands/cluster_register.go +++ b/internal/commands/cluster_register.go @@ -53,7 +53,7 @@ var ( var ClusterRegisterCommand = &cli.Command{ Name: "register", Usage: "Registers a node or updates its credentials within a cluster", - Flags: append(append([]cli.Flag{ + Flags: append([]cli.Flag{ regDryRun, regNameFlag, regRoleFlag, @@ -68,7 +68,7 @@ var ClusterRegisterCommand = &cli.Command{ regRotateSec, regWriteConf, regForceFlag, - }, report.CliFlags...)), + }, report.CliFlags...), Action: clusterRegisterAction, } diff --git a/internal/commands/cluster_theme_pull.go b/internal/commands/cluster_theme_pull.go index 8c257cd2b..1e5eafcc7 100644 --- a/internal/commands/cluster_theme_pull.go +++ b/internal/commands/cluster_theme_pull.go @@ -320,22 +320,18 @@ func unzipSafe(zipPath, dest string) error { if err != nil { return err } + defer rc.Close() // Create/truncate target - out, err := os.OpenFile(target, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, f.Mode()) + out, err := os.OpenFile(target, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, f.Mode()) //nolint:gosec // paths derived from zip entries validated earlier if err != nil { - rc.Close() return err } + defer out.Close() - if _, err := io.Copy(out, rc); err != nil { - out.Close() - rc.Close() + if _, err := io.Copy(out, rc); err != nil { //nolint:gosec // zip entries size is bounded by upstream return err } - - out.Close() - rc.Close() } return nil } diff --git a/internal/commands/commands.go b/internal/commands/commands.go index 87d16538b..1c0b57999 100644 --- a/internal/commands/commands.go +++ b/internal/commands/commands.go @@ -38,6 +38,7 @@ import ( "github.com/photoprism/photoprism/pkg/fs" ) +// NONINTERACTIVE is the CLI environment flag to disable prompts. const NONINTERACTIVE = "noninteractive" var log = event.Log diff --git a/internal/commands/download.go b/internal/commands/download.go index c7e3cd151..f1baf9c00 100644 --- a/internal/commands/download.go +++ b/internal/commands/download.go @@ -160,9 +160,10 @@ func downloadAction(ctx *cli.Context) error { impersonate := strings.ToLower(strings.TrimSpace(ctx.String("impersonate"))) - if impersonate == "" { + switch impersonate { + case "": impersonate = "firefox" - } else if impersonate == "none" { + case "none": impersonate = "" } @@ -282,14 +283,14 @@ func downloadAction(ctx *cli.Context) error { } func() { defer downloadResult.Close() - f, ferr := os.Create(downloadFilePath) + f, ferr := os.Create(downloadFilePath) //nolint:gosec // download target path chosen by user if ferr != nil { log.Errorf("create file failed: %v", ferr) failures++ return } + defer f.Close() if _, cerr := io.Copy(f, downloadResult); cerr != nil { - _ = f.Close() log.Errorf("write file failed: %v", cerr) failures++ return diff --git a/internal/commands/download_format_test.go b/internal/commands/download_format_test.go index aed081673..bc5e212d9 100644 --- a/internal/commands/download_format_test.go +++ b/internal/commands/download_format_test.go @@ -25,7 +25,7 @@ func createArgsLoggingYtDlp(t *testing.T) string { b.WriteString(" if \"%%~A\"==\"--version\" ( echo 2025.09.23 & goto :eof )\r\n") b.WriteString(" if \"%%~A\"==\"--dump-single-json\" ( echo {\"id\":\"abc\",\"title\":\"Test\",\"url\":\"http://example.com\",\"_type\":\"video\"} & goto :eof )\r\n") b.WriteString(")\r\n") - if err := os.WriteFile(path, []byte(b.String()), 0o755); err != nil { + if err := os.WriteFile(path, []byte(b.String()), 0o600); err != nil { t.Fatalf("failed to write fake yt-dlp: %v", err) } return path @@ -44,7 +44,7 @@ func createArgsLoggingYtDlp(t *testing.T) string { b.WriteString("echo '[download]' 1>&2\n") b.WriteString("echo 'DATA'\n") - if err := os.WriteFile(path, []byte(b.String()), 0o755); err != nil { + if err := os.WriteFile(path, []byte(b.String()), 0o600); err != nil { t.Fatalf("failed to write fake yt-dlp: %v", err) } return path @@ -98,7 +98,7 @@ func TestRunDownload_FileMethod_OmitsFormatSort(t *testing.T) { // Give the logging script a moment to flush in slower environments. time.Sleep(20 * time.Millisecond) - data, err := os.ReadFile(argsLog) + data, err := os.ReadFile(argsLog) //nolint:gosec // test temp file if err != nil { t.Fatalf("reading args log failed: %v", err) } @@ -157,7 +157,7 @@ func TestRunDownload_FileMethod_WithFormatSort(t *testing.T) { time.Sleep(20 * time.Millisecond) - data, err := os.ReadFile(argsLog) + data, err := os.ReadFile(argsLog) //nolint:gosec // test temp file if err != nil { t.Fatalf("reading args log failed: %v", err) } diff --git a/internal/commands/download_impl.go b/internal/commands/download_impl.go index 638d0bcbe..adeb376eb 100644 --- a/internal/commands/download_impl.go +++ b/internal/commands/download_impl.go @@ -184,7 +184,7 @@ func runDownload(conf *config.Config, opts DownloadOpts, inputURLs []string) err } func() { defer downloadResult.Close() - f, ferr := os.Create(downloadFilePath) + f, ferr := os.Create(downloadFilePath) //nolint:gosec // download target path chosen by user if ferr != nil { log.Errorf("create file failed: %v", ferr) failures++ diff --git a/internal/commands/find.go b/internal/commands/find.go index e96714b7c..0a04bbe28 100644 --- a/internal/commands/find.go +++ b/internal/commands/find.go @@ -74,7 +74,11 @@ func findAction(ctx *cli.Context) error { rows := make([][]string, 0, len(results)) for _, found := range results { - v := []string{found.FileName, found.FileMime, humanize.Bytes(uint64(found.FileSize)), found.FileHash} + size := found.FileSize + if size < 0 { + size = 0 + } + v := []string{found.FileName, found.FileMime, humanize.Bytes(uint64(size)), found.FileHash} //nolint:gosec // size non-negative after check rows = append(rows, v) } diff --git a/internal/commands/find_test.go b/internal/commands/find_test.go index 32a9e855b..9efaa0c7c 100644 --- a/internal/commands/find_test.go +++ b/internal/commands/find_test.go @@ -12,7 +12,7 @@ func TestFindCommand(t *testing.T) { output, err := RunWithTestContext(FindCommand, []string{"find", "--csv"}) // Check command output for plausibility. - //t.Logf(output) + // t.Logf(output) assert.NoError(t, err) assert.Contains(t, output, "File Name;Mime Type;") }) diff --git a/internal/commands/migrations.go b/internal/commands/migrations.go index c5eddc6f7..1a1bb3697 100644 --- a/internal/commands/migrations.go +++ b/internal/commands/migrations.go @@ -16,6 +16,7 @@ import ( "github.com/photoprism/photoprism/pkg/txt/report" ) +// MigrationsStatusCommand lists migration status. var MigrationsStatusCommand = &cli.Command{ Name: "ls", Aliases: []string{"status", "show"}, @@ -25,6 +26,7 @@ var MigrationsStatusCommand = &cli.Command{ Action: migrationsStatusAction, } +// MigrationsRunCommand runs pending migrations. var MigrationsRunCommand = &cli.Command{ Name: "run", Aliases: []string{"execute", "migrate"}, @@ -110,15 +112,16 @@ func migrationsStatusAction(ctx *cli.Context) error { finished = "-" } - if m.Error != "" { + switch { + case m.Error != "": info = m.Error - } else if m.Finished() { + case m.Finished(): info = "OK" - } else if m.StartedAt.IsZero() { + case m.StartedAt.IsZero(): info = "-" - } else if m.Repeat(false) { + case m.Repeat(false): info = "Repeat" - } else { + default: info = "Running?" } diff --git a/internal/commands/passwd.go b/internal/commands/passwd.go index 0ec942b9b..c34d8d1e5 100644 --- a/internal/commands/passwd.go +++ b/internal/commands/passwd.go @@ -131,12 +131,12 @@ func termEcho(on bool) { Sys: nil} var ws syscall.WaitStatus cmd := "echo" - if on == false { + if !on { cmd = "-echo" } // Enable/disable echoing. - pid, err := syscall.ForkExec( + pid, err := syscall.ForkExec( //nolint:gosec // uses fixed binary and arguments "/bin/stty", []string{"stty", cmd}, &attrs) diff --git a/internal/commands/show_config.go b/internal/commands/show_config.go index 1c5f95732..2d720fe7c 100644 --- a/internal/commands/show_config.go +++ b/internal/commands/show_config.go @@ -65,9 +65,10 @@ func showConfigAction(ctx *cli.Context) error { rows, cols := rep.Report(conf) opt := report.Options{Format: format, NoWrap: rep.NoWrap} result, _ := report.Render(rows, cols, opt) - if opt.Format == report.Markdown { + switch opt.Format { + case report.Markdown: fmt.Printf("### %s\n\n", rep.Title) - } else if opt.Format == report.Default { + case report.Default: fmt.Printf("%s\n\n", strings.ToUpper(rep.Title)) } fmt.Println(result) diff --git a/internal/commands/show_scopes.go b/internal/commands/show_scopes.go index 0c2c7301f..9ac99c315 100644 --- a/internal/commands/show_scopes.go +++ b/internal/commands/show_scopes.go @@ -13,7 +13,7 @@ import ( var ShowScopesCommand = &cli.Command{ Name: "scopes", Usage: "Displays supported authorization scopes", - Flags: append(report.CliFlags), + Flags: report.CliFlags, Action: showScopesAction, } diff --git a/internal/commands/show_sources.go b/internal/commands/show_sources.go index 1bf0cac87..ec398826a 100644 --- a/internal/commands/show_sources.go +++ b/internal/commands/show_sources.go @@ -13,7 +13,7 @@ import ( var ShowSourcesCommand = &cli.Command{ Name: "sources", Usage: "Displays supported metadata sources and their priorities", - Flags: append(report.CliFlags), + Flags: report.CliFlags, Action: showSourcesAction, } diff --git a/internal/commands/status.go b/internal/commands/status.go index 27a37b36c..b6ed1ffa3 100644 --- a/internal/commands/status.go +++ b/internal/commands/status.go @@ -57,7 +57,7 @@ func statusAction(ctx *cli.Context) error { if resp, reqErr := client.Do(req); reqErr != nil { return fmt.Errorf("cannot connect to %s:%d", conf.HttpHost(), conf.HttpPort()) } else if resp.StatusCode != 200 { - return fmt.Errorf("server running at %s:%d, bad status %d\n", conf.HttpHost(), conf.HttpPort(), resp.StatusCode) + return fmt.Errorf("server running at %s:%d, bad status %d", conf.HttpHost(), conf.HttpPort(), resp.StatusCode) } else if body, readErr := io.ReadAll(resp.Body); readErr != nil { return readErr } else { diff --git a/internal/commands/users.go b/internal/commands/users.go index 9b8613710..c2ebeb0db 100644 --- a/internal/commands/users.go +++ b/internal/commands/users.go @@ -23,6 +23,7 @@ const ( ) var ( + // UserRoleUsage describes allowed user roles for CLI help. UserRoleUsage = fmt.Sprintf("user account `ROLE`, e.g. %s", acl.UserRoles.CliUsageString()) ) diff --git a/internal/commands/users_add.go b/internal/commands/users_add.go index 38b57c18b..c73621cb9 100644 --- a/internal/commands/users_add.go +++ b/internal/commands/users_add.go @@ -134,10 +134,6 @@ func usersAddAction(ctx *cli.Context) error { } } - if err := entity.AddUser(frm); err != nil { - return err - } - - return nil + return entity.AddUser(frm) }) } diff --git a/internal/commands/users_legacy_test.go b/internal/commands/users_legacy_test.go index c6cc22f6e..bbf5f8ff1 100644 --- a/internal/commands/users_legacy_test.go +++ b/internal/commands/users_legacy_test.go @@ -12,7 +12,7 @@ func TestUsersLegacyCommand(t *testing.T) { output, err := RunWithTestContext(UsersLegacyCommand, []string{""}) // Check command output for plausibility. - //t.Logf(output) + // t.Logf(output) assert.NoError(t, err) assert.Contains(t, output, "│ ID │ UID │ Name │ User │ Email │ Admin │ Created At │") }) diff --git a/internal/commands/users_reset.go b/internal/commands/users_reset.go index 6f91ef671..68a478ed3 100644 --- a/internal/commands/users_reset.go +++ b/internal/commands/users_reset.go @@ -9,6 +9,7 @@ import ( "github.com/photoprism/photoprism/internal/entity" ) +// UsersResetDescription explains the effect of the users reset command. const UsersResetDescription = "This command recreates the session and user management database tables so that they are compatible with the current version. Should you experience login problems, for example after an upgrade from an earlier version or a development preview, we recommend that you first try the \"photoprism auth reset --yes\" command to see if it solves the issue. Note that any client access tokens and app passwords that users may have created are also deleted and must be recreated." // UsersResetCommand configures the command name, flags, and action. diff --git a/internal/commands/users_reset_test.go b/internal/commands/users_reset_test.go index 745da4727..768160e7f 100644 --- a/internal/commands/users_reset_test.go +++ b/internal/commands/users_reset_test.go @@ -21,7 +21,7 @@ func TestUsersResetCommand(t *testing.T) { output, err := RunWithTestContext(UsersResetCommand, []string{"reset"}) // Check command output for plausibility. - //t.Logf(output) + // t.Logf(output) assert.NoError(t, err) assert.Empty(t, output) diff --git a/internal/commands/users_show_test.go b/internal/commands/users_show_test.go index cba3dd32d..7f0a4af7b 100644 --- a/internal/commands/users_show_test.go +++ b/internal/commands/users_show_test.go @@ -12,7 +12,7 @@ func TestUsersShowCommand(t *testing.T) { output, err := RunWithTestContext(UsersShowCommand, []string{"show", "alice"}) // Check command output for plausibility. - //t.Logf(output) + // t.Logf(output) assert.NoError(t, err) assert.Contains(t, output, "Alice") assert.Contains(t, output, "admin") diff --git a/internal/commands/users_test.go b/internal/commands/users_test.go index cf23ea19b..bc30ced40 100644 --- a/internal/commands/users_test.go +++ b/internal/commands/users_test.go @@ -20,7 +20,7 @@ func TestUsersCommand(t *testing.T) { // Run command with test context. output2, err := RunWithTestContext(UsersShowCommand, []string{"show", "john.admin"}) - //t.Logf(output2) + // t.Logf(output2) assert.NoError(t, err) assert.Contains(t, output2, "John") assert.Contains(t, output2, "admin") @@ -38,7 +38,7 @@ func TestUsersCommand(t *testing.T) { // Run command with test context. output4, err := RunWithTestContext(UsersShowCommand, []string{"show", "john.admin"}) - //t.Logf(output4) + // t.Logf(output4) assert.NoError(t, err) assert.Contains(t, output4, "Johnny") assert.Contains(t, output4, "admin") @@ -57,7 +57,7 @@ func TestUsersCommand(t *testing.T) { // Run command with test context. output6, err := RunWithTestContext(UsersShowCommand, []string{"show", "john.admin"}) - //t.Logf(output6) + // t.Logf(output6) assert.NoError(t, err) assert.Contains(t, output6, "Johnny") assert.Contains(t, output6, "admin") diff --git a/internal/commands/vision.go b/internal/commands/vision.go index 531d17b25..a52a4ec37 100644 --- a/internal/commands/vision.go +++ b/internal/commands/vision.go @@ -23,6 +23,6 @@ var VisionCommands = &cli.Command{ var VisionSourcesCommand = &cli.Command{ Name: "sources", Usage: "Displays supported metadata sources and their priorities", - Flags: append(report.CliFlags), + Flags: report.CliFlags, Action: showSourcesAction, } diff --git a/internal/commands/vision_list.go b/internal/commands/vision_list.go index 46f4f0620..9add1311e 100644 --- a/internal/commands/vision_list.go +++ b/internal/commands/vision_list.go @@ -17,7 +17,7 @@ import ( var VisionListCommand = &cli.Command{ Name: "ls", Usage: "Lists the configured computer vision models", - Flags: append(report.CliFlags), + Flags: report.CliFlags, Action: visionListAction, } From 57c9096d1f2ec0e014a87cc9fcf3de38f5af2a82 Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sat, 22 Nov 2025 17:57:25 +0100 Subject: [PATCH 039/195] CI: Apply Go linter recommendations to "internal/service" packages #5330 Signed-off-by: Michael Mayer --- internal/service/cluster/const.go | 10 +++++----- internal/service/cluster/options_update.go | 1 - internal/service/cluster/options_update_test.go | 2 +- internal/service/cluster/policy.go | 2 +- internal/service/cluster/provisioner/database.go | 2 +- .../service/cluster/provisioner/database_test.go | 4 ++-- internal/service/cluster/provisioner/naming.go | 3 --- internal/service/cluster/registry/node.go | 7 ++++--- internal/service/cluster/roles.go | 10 +++++++--- internal/service/cluster/theme/version.go | 2 +- internal/service/heuristic.go | 5 ++++- internal/service/hub/config.go | 12 ++++++++---- internal/service/hub/places/cell.go | 1 + internal/service/hub/places/config.go | 9 +++++++-- internal/service/hub/places/errors.go | 3 +++ internal/service/hub/places/latlng.go | 1 + internal/service/hub/places/request.go | 4 ++-- internal/service/hub/places/search.go | 1 + internal/service/hub/places/search_result.go | 2 ++ internal/service/maps/countries.go | 1 + internal/service/maps/gen.go | 2 +- internal/service/maps/location.go | 6 +++--- internal/service/maps/maps.go | 6 ------ internal/service/webdav/client.go | 4 ++-- internal/service/webdav/webdav.go | 3 +++ 25 files changed, 61 insertions(+), 42 deletions(-) diff --git a/internal/service/cluster/const.go b/internal/service/cluster/const.go index 23ca8cf31..036bd86fd 100644 --- a/internal/service/cluster/const.go +++ b/internal/service/cluster/const.go @@ -10,12 +10,12 @@ const ( // Example values used by documentation and tests to illustrate cluster tokens. const ( - // ExampleJoinToken represents a valid portal join token. - ExampleJoinToken = "pGVplw8-eISgkdQN-Mep62nQ" - // ExampleJoinTokenAlt provides an alternative join token for negative/rotation tests. - ExampleJoinTokenAlt = "k9sEFe6-A7gt6zqm-gY9gFh0" + // ExampleJoinToken represents a valid portal join token. Sample only. + ExampleJoinToken = "pGVplw8-eISgkdQN-Mep62nQ" //nolint:gosec // example value, not a secret + // ExampleJoinTokenAlt provides an alternative join token for negative/rotation tests. Sample only. + ExampleJoinTokenAlt = "k9sEFe6-A7gt6zqm-gY9gFh0" //nolint:gosec // example value, not a secret // ExampleClientID is a sample node client identifier issued by the portal. ExampleClientID = "cs5gfen1bgxz7s9i" // ExampleClientSecret is a sample node client secret matching the format generated by rnd.ClientSecret(). - ExampleClientSecret = "A1B2C3D4E5F6G7H8J9K0L1M2N3P4Q5R6" + ExampleClientSecret = "A1B2C3D4E5F6G7H8J9K0L1M2N3P4Q5R6" //nolint:gosec // example value ) diff --git a/internal/service/cluster/options_update.go b/internal/service/cluster/options_update.go index 799e9e4fb..22889642b 100644 --- a/internal/service/cluster/options_update.go +++ b/internal/service/cluster/options_update.go @@ -105,7 +105,6 @@ func (u *OptionsUpdate) SetDatabasePassword(value string) { u.DatabasePassword = stringPtr(value) } -// forEach enumerates all set fields and invokes fn with the corresponding key/value pair. // Visit enumerates all set fields and invokes fn with the corresponding key/value pair. func (u OptionsUpdate) Visit(fn func(string, any)) { if u.ClusterUUID != nil { diff --git a/internal/service/cluster/options_update_test.go b/internal/service/cluster/options_update_test.go index d81ce9d38..b9526ca51 100644 --- a/internal/service/cluster/options_update_test.go +++ b/internal/service/cluster/options_update_test.go @@ -38,7 +38,7 @@ func TestOptionsUpdate_Apply(t *testing.T) { seed := map[string]any{"Existing": "value"} b, err := yaml.Marshal(seed) require.NoError(t, err) - require.NoError(t, os.WriteFile(conf.OptionsYaml(), b, 0o644)) + require.NoError(t, os.WriteFile(conf.OptionsYaml(), b, 0o600)) update := cluster.OptionsUpdate{} update.SetClusterUUID("4a47c940-d5de-41b3-88a2-eb816cc659ca") diff --git a/internal/service/cluster/policy.go b/internal/service/cluster/policy.go index 60430b9dc..7d0679855 100644 --- a/internal/service/cluster/policy.go +++ b/internal/service/cluster/policy.go @@ -33,7 +33,7 @@ func init() { applyPolicyEnv() } -// applyPolicyEnv allows advanced users to fine-tune bootstrap behaviour via environment +// applyPolicyEnv allows advanced users to fine-tune bootstrap behavior via environment // variables without exposing additional user-facing configuration options. func applyPolicyEnv() { if v := os.Getenv(clean.EnvVar("cluster-bootstrap-auto-join-enabled")); v != "" { diff --git a/internal/service/cluster/provisioner/database.go b/internal/service/cluster/provisioner/database.go index 18058ed85..feb1cfee4 100644 --- a/internal/service/cluster/provisioner/database.go +++ b/internal/service/cluster/provisioner/database.go @@ -10,7 +10,7 @@ import ( "sync" "time" - _ "github.com/go-sql-driver/mysql" + _ "github.com/go-sql-driver/mysql" // register MySQL driver ) // ProvisionDSN specifies the admin DSN used for auto-provisioning, for example: diff --git a/internal/service/cluster/provisioner/database_test.go b/internal/service/cluster/provisioner/database_test.go index 0c6e855b0..8cab5dd50 100644 --- a/internal/service/cluster/provisioner/database_test.go +++ b/internal/service/cluster/provisioner/database_test.go @@ -110,7 +110,7 @@ func TestExecTimeout_DeadlineExceeded(t *testing.T) { // execTimeout forwards the statement correctly. type fastDriver struct{ last *string } -func (f fastDriver) Open(name string) (driver.Conn, error) { return fastConn{last: f.last}, nil } +func (f fastDriver) Open(name string) (driver.Conn, error) { return fastConn(f), nil } type fastConn struct{ last *string } @@ -147,7 +147,7 @@ func TestExecTimeout_ForwardsStatement(t *testing.T) { // variant waits for cancellation, the other returns quickly. type pingyDriver struct{ wait bool } -func (p pingyDriver) Open(name string) (driver.Conn, error) { return pingyConn{wait: p.wait}, nil } +func (p pingyDriver) Open(name string) (driver.Conn, error) { return pingyConn(p), nil } type pingyConn struct{ wait bool } diff --git a/internal/service/cluster/provisioner/naming.go b/internal/service/cluster/provisioner/naming.go index 89cc3021a..0a486522f 100644 --- a/internal/service/cluster/provisioner/naming.go +++ b/internal/service/cluster/provisioner/naming.go @@ -20,9 +20,6 @@ const ( // username: cluster_u dbSuffix = 11 userSuffix = 11 - // Budgets: keep user conservative for MySQL compatibility; MariaDB allows more. - userMax = 32 - dbMax = 64 // prefixMax ensures usernames remain within the MySQL identifier limit. prefixMax = cluster.DatabaseProvisionPrefixMaxLen ) diff --git a/internal/service/cluster/registry/node.go b/internal/service/cluster/registry/node.go index b6e22e296..b93685cd8 100644 --- a/internal/service/cluster/registry/node.go +++ b/internal/service/cluster/registry/node.go @@ -13,8 +13,9 @@ type Node struct { // ensureDatabase returns a writable NodeDatabase, creating one if missing. func (n *Node) ensureDatabase() *cluster.NodeDatabase { - if n.Node.Database == nil { - n.Node.Database = &cluster.NodeDatabase{} + if n.Database == nil { + n.Database = &cluster.NodeDatabase{} } - return n.Node.Database + + return n.Database } diff --git a/internal/service/cluster/roles.go b/internal/service/cluster/roles.go index 5d3771930..adf5845eb 100644 --- a/internal/service/cluster/roles.go +++ b/internal/service/cluster/roles.go @@ -4,10 +4,14 @@ import ( "github.com/photoprism/photoprism/internal/auth/acl" ) +// NodeRole represents the role a node plays within a cluster. type NodeRole = string const ( - RoleApp = NodeRole(acl.RoleApp) // A regular PhotoPrism app node that can join a cluster - RolePortal = NodeRole(acl.RolePortal) // A management portal for orchestrating a cluster - RoleService = NodeRole(acl.RoleService) // Other service used within a cluster, e.g. Ollama or Vision API + // RoleApp represents a regular PhotoPrism app node that can join a cluster. + RoleApp = NodeRole(acl.RoleApp) + // RolePortal represents a management portal for orchestrating a cluster. + RolePortal = NodeRole(acl.RolePortal) + // RoleService represents other services used within a cluster, e.g., Ollama or Vision API. + RoleService = NodeRole(acl.RoleService) ) diff --git a/internal/service/cluster/theme/version.go b/internal/service/cluster/theme/version.go index 52a4ced94..2fef37b83 100644 --- a/internal/service/cluster/theme/version.go +++ b/internal/service/cluster/theme/version.go @@ -29,7 +29,7 @@ func DetectVersion(themePath string) (string, error) { versionFile := filepath.Join(themePath, fs.VersionTxtFile) - if data, readErr := os.ReadFile(versionFile); readErr == nil { + if data, readErr := os.ReadFile(versionFile); readErr == nil { //nolint:gosec // version file path is internal, provided by caller if v := clean.TypeUnicode(string(data)); v != "" { return v, nil } diff --git a/internal/service/heuristic.go b/internal/service/heuristic.go index 14354705f..098eadbee 100644 --- a/internal/service/heuristic.go +++ b/internal/service/heuristic.go @@ -6,6 +6,7 @@ import ( "strings" ) +// Headers holds request header key/value pairs. type Headers = map[string]string // Heuristic represents a heuristic for detecting a remote service type, e.g. WebDAV. @@ -36,6 +37,7 @@ var Heuristics = []Heuristic{ }, } +// MatchDomain returns true if the heuristic allows the provided domain. func (h Heuristic) MatchDomain(match string) bool { if len(h.Domains) == 0 { return true @@ -44,6 +46,7 @@ func (h Heuristic) MatchDomain(match string) bool { return slices.Contains(h.Domains, match) } +// Discover returns the first matching endpoint URL for the heuristic. func (h Heuristic) Discover(rawUrl, user string) *url.URL { u, err := url.Parse(rawUrl) @@ -56,7 +59,7 @@ func (h Heuristic) Discover(rawUrl, user string) *url.URL { } for _, p := range h.Paths { - u.Path = strings.Replace(p, "{user}", user, -1) + u.Path = strings.ReplaceAll(p, "{user}", user) if h.TestRequest(h.Method, u.String()) { return u diff --git a/internal/service/hub/config.go b/internal/service/hub/config.go index a7325ebe1..f5b632e1b 100644 --- a/internal/service/hub/config.go +++ b/internal/service/hub/config.go @@ -4,7 +4,7 @@ import ( "bytes" "crypto/aes" "crypto/cipher" - "crypto/sha1" + "crypto/sha1" //nolint:gosec // retained for legacy key derivation check "crypto/sha256" "encoding/hex" "encoding/json" @@ -25,11 +25,15 @@ import ( "github.com/photoprism/photoprism/pkg/http/header" ) +// Status represents the hub registration state. type Status string const ( - StatusUnknown Status = "" - StatusNew Status = "unregistered" + // StatusUnknown indicates an undefined state. + StatusUnknown Status = "" + // StatusNew indicates a node has not registered yet. + StatusNew Status = "unregistered" + // StatusCommunity indicates Community Edition status. StatusCommunity Status = "ce" ) @@ -122,7 +126,7 @@ func (c *Config) Sanitize() { c.Key = strings.ToLower(c.Key) if c.Secret != "" { - if c.Key != fmt.Sprintf("%x", sha1.Sum([]byte(c.Secret))) { + if c.Key != fmt.Sprintf("%x", sha1.Sum([]byte(c.Secret))) { //nolint:gosec // legacy SHA1 comparison c.Key = "" c.Secret = "" c.Session = "" diff --git a/internal/service/hub/places/cell.go b/internal/service/hub/places/cell.go index 98a872c6e..f039a0739 100644 --- a/internal/service/hub/places/cell.go +++ b/internal/service/hub/places/cell.go @@ -1,3 +1,4 @@ +//nolint:gocritic // explicit branching retained for clarity package places import ( diff --git a/internal/service/hub/places/config.go b/internal/service/hub/places/config.go index e7218150d..e04673b48 100644 --- a/internal/service/hub/places/config.go +++ b/internal/service/hub/places/config.go @@ -28,6 +28,11 @@ var Retries = 2 // RetryDelay specifies the waiting time between retries. var RetryDelay = 100 * time.Millisecond -var Key = "f60f5b25d59c397989e3cd374f81cdd7710a4fca" -var Secret = "photoprism" +// Key is the hub places API key (overridden via environment/config in production). +var Key = "f60f5b25d59c397989e3cd374f81cdd7710a4fca" //nolint:gosec // example/default key + +// Secret is the hub places API secret (overridden in production). +var Secret = "photoprism" //nolint:gosec // example/default secret + +// UserAgent overrides the default HTTP User-Agent header for hub places calls. var UserAgent = "" diff --git a/internal/service/hub/places/errors.go b/internal/service/hub/places/errors.go index 3476301c8..bf0441807 100644 --- a/internal/service/hub/places/errors.go +++ b/internal/service/hub/places/errors.go @@ -4,5 +4,8 @@ import ( "errors" ) +// ErrMissingQuery indicates that a place search query was empty. var ErrMissingQuery = errors.New("missing query") + +// ErrMissingCoordinates indicates that both latitude and longitude were missing. var ErrMissingCoordinates = errors.New("missing coordinates") diff --git a/internal/service/hub/places/latlng.go b/internal/service/hub/places/latlng.go index c83a1e45b..d00389f38 100644 --- a/internal/service/hub/places/latlng.go +++ b/internal/service/hub/places/latlng.go @@ -1,3 +1,4 @@ +//nolint:gocritic // explicit branching retained for clarity package places import ( diff --git a/internal/service/hub/places/request.go b/internal/service/hub/places/request.go index dc64d71ca..f560cb481 100644 --- a/internal/service/hub/places/request.go +++ b/internal/service/hub/places/request.go @@ -1,7 +1,7 @@ package places import ( - "crypto/sha1" + "crypto/sha1" //nolint:gosec // required for upstream signature scheme "fmt" "net/http" "time" @@ -40,7 +40,7 @@ func GetRequest(reqUrl string, locale string) (r *http.Response, err error) { // Add API key? if Key != "" { req.Header.Set("X-Key", Key) - req.Header.Set("X-Signature", fmt.Sprintf("%x", sha1.Sum([]byte(Key+reqUrl+Secret)))) + req.Header.Set("X-Signature", fmt.Sprintf("%x", sha1.Sum([]byte(Key+reqUrl+Secret)))) //nolint:gosec // upstream expects SHA1 } // Create new http.Client. diff --git a/internal/service/hub/places/search.go b/internal/service/hub/places/search.go index fa7306160..a5f6e39cb 100644 --- a/internal/service/hub/places/search.go +++ b/internal/service/hub/places/search.go @@ -1,3 +1,4 @@ +//nolint:gocritic // explicit branching retained for clarity package places import ( diff --git a/internal/service/hub/places/search_result.go b/internal/service/hub/places/search_result.go index 4314c99ba..f38d1d87f 100644 --- a/internal/service/hub/places/search_result.go +++ b/internal/service/hub/places/search_result.go @@ -1,5 +1,6 @@ package places +// SearchResult represents a place returned by the hub places API. type SearchResult struct { Id string `json:"id"` Name string `json:"name,omitempty"` @@ -12,4 +13,5 @@ type SearchResult struct { Licence string `json:"licence,omitempty"` } +// SearchResults is a slice of SearchResult. type SearchResults = []SearchResult diff --git a/internal/service/maps/countries.go b/internal/service/maps/countries.go index a670bc49b..2a63ef61b 100644 --- a/internal/service/maps/countries.go +++ b/internal/service/maps/countries.go @@ -2,6 +2,7 @@ package maps // Generated code, do not edit. +// CountryNames maps ISO country codes to localized names. var CountryNames = map[string]string{ "af": "Afghanistan", "ax": "Åland Islands", diff --git a/internal/service/maps/gen.go b/internal/service/maps/gen.go index af72a4465..70166a16e 100644 --- a/internal/service/maps/gen.go +++ b/internal/service/maps/gen.go @@ -1,5 +1,4 @@ //go:build ignore -// +build ignore // This generates countries.go by running "go generate" package main @@ -55,6 +54,7 @@ package maps // Generated code, do not edit. +// CountryNames maps ISO country codes to localized names. var CountryNames = map[string]string{ {{- range .Countries }} {{ printf "%q" .Code }}: {{ printf "%q" .Name }}, diff --git a/internal/service/maps/location.go b/internal/service/maps/location.go index 1ec4a1c36..adb876b0a 100644 --- a/internal/service/maps/location.go +++ b/internal/service/maps/location.go @@ -1,3 +1,4 @@ +//revive:disable:exported // getters intentionally exported for use across services package maps import ( @@ -42,9 +43,8 @@ type LocationSource interface { Source() string } -func (l *Location) QueryApi(api string) error { - switch api { - case places.ApiName: +func (l *Location) QueryApi(api string) error { //nolint:gocritic // single-case switch retained for future APIs + if api == places.ApiName { return l.QueryPlaces() } diff --git a/internal/service/maps/maps.go b/internal/service/maps/maps.go index 28da56dd4..36f2caa03 100644 --- a/internal/service/maps/maps.go +++ b/internal/service/maps/maps.go @@ -24,11 +24,5 @@ Additional information can be found in our Developer Guide: */ package maps -import ( - "github.com/photoprism/photoprism/internal/event" -) - //go:generate go run gen.go //go:generate go fmt . - -var log = event.Log diff --git a/internal/service/webdav/client.go b/internal/service/webdav/client.go index 3221aa633..28bd62eac 100644 --- a/internal/service/webdav/client.go +++ b/internal/service/webdav/client.go @@ -221,7 +221,7 @@ func (c *Client) Upload(src, dest string) (err error) { return fmt.Errorf("file %s not found", clean.Log(path.Base(src))) } - f, err := os.OpenFile(src, os.O_RDONLY, 0) + f, err := os.OpenFile(src, os.O_RDONLY, 0) //nolint:gosec // path provided by caller; read-only if err != nil { log.Errorf("webdav: %s", clean.Error(err)) @@ -290,7 +290,7 @@ func (c *Client) Download(src, dest string, force bool) (err error) { defer reader.Close() - f, err := os.OpenFile(dest, os.O_TRUNC|os.O_RDWR|os.O_CREATE, fs.ModeFile) + f, err := os.OpenFile(dest, os.O_TRUNC|os.O_RDWR|os.O_CREATE, fs.ModeFile) //nolint:gosec // dest provided by caller if err != nil { log.Errorf("webdav: %s", clean.Error(err)) diff --git a/internal/service/webdav/webdav.go b/internal/service/webdav/webdav.go index 02579b502..067ccab35 100644 --- a/internal/service/webdav/webdav.go +++ b/internal/service/webdav/webdav.go @@ -33,6 +33,7 @@ import ( // Global log instance. var log = event.Log +// Timeout specifies a timeout mode for WebDAV operations. type Timeout string // Request Timeout options. @@ -45,6 +46,8 @@ const ( ) // Second represents a second on which other timeouts are based. +// +//revive:disable-next-line:time-naming // keep exported constant name for API compatibility const Second = time.Second // MaxRequestDuration is the maximum request duration e.g. for recursive retrieval of large remote directory structures. From 7c0f0b41badd1395d787b4a5c3ad6060352d8a4f Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sat, 22 Nov 2025 20:00:53 +0100 Subject: [PATCH 040/195] CI: Apply Go linter recommendations to "internal/config" packages #5330 Signed-off-by: Michael Mayer --- internal/config/cli_context.go | 2 +- internal/config/cli_context_test.go | 45 +++++++++++++++++++++++++ internal/config/cli_flag.go | 11 +++--- internal/config/client_assets.go | 8 ++--- internal/config/client_config.go | 11 +++--- internal/config/config.go | 31 ++++++++--------- internal/config/config_auth.go | 18 ++++++---- internal/config/config_backup.go | 4 ++- internal/config/config_cache.go | 5 ++- internal/config/config_cluster.go | 12 ++++--- internal/config/config_cluster_test.go | 10 +++--- internal/config/config_const.go | 14 ++++---- internal/config/config_customize.go | 13 ++++--- internal/config/config_db.go | 13 +++---- internal/config/config_faces.go | 2 +- internal/config/config_faces_test.go | 4 +-- internal/config/config_features.go | 3 ++ internal/config/config_features_test.go | 5 --- internal/config/config_ffmpeg.go | 20 ++++++----- internal/config/config_oidc.go | 17 ++++++---- internal/config/config_server.go | 8 ++--- internal/config/config_site.go | 2 +- internal/config/config_thumb.go | 7 ++-- internal/config/config_tls.go | 2 ++ internal/config/config_usage_test.go | 2 +- internal/config/config_vision.go | 2 +- internal/config/develop.go | 1 - internal/config/err.go | 2 ++ internal/config/extensions_test.go | 7 ++-- internal/config/messages.go | 10 ++++-- internal/config/options.go | 5 ++- internal/config/pwa/icon.go | 7 ++-- internal/config/report.go | 13 +++---- internal/config/schedule.go | 8 +++-- internal/config/test.go | 10 +++--- internal/config/ttl/cache.go | 12 ++++--- internal/config/var.go | 6 ++-- 37 files changed, 219 insertions(+), 133 deletions(-) create mode 100644 internal/config/cli_context_test.go diff --git a/internal/config/cli_context.go b/internal/config/cli_context.go index 409752f55..353765796 100644 --- a/internal/config/cli_context.go +++ b/internal/config/cli_context.go @@ -35,7 +35,7 @@ func ApplyCliContext(c interface{}, ctx *cli.Context) error { if s == "" { // Omit. } else if sec := txt.UInt(s); sec > 0 { - fieldValue.Set(reflect.ValueOf(time.Duration(sec) * time.Second)) + fieldValue.Set(reflect.ValueOf(time.Duration(sec) * time.Second)) //nolint:gosec // txt.UInt is bounded; duration uses int64 on supported platforms } else if d, err := time.ParseDuration(s); err == nil { fieldValue.Set(reflect.ValueOf(d)) } diff --git a/internal/config/cli_context_test.go b/internal/config/cli_context_test.go new file mode 100644 index 000000000..c5c068a5f --- /dev/null +++ b/internal/config/cli_context_test.go @@ -0,0 +1,45 @@ +package config + +import ( + "flag" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/urfave/cli/v2" +) + +type durationTarget struct { + Interval time.Duration `flag:"interval"` +} + +func TestApplyCliContext_Duration(t *testing.T) { + tests := []struct { + name string + input string + expected time.Duration + }{ + {name: "WithUnits", input: "1h30m", expected: 90 * time.Minute}, + {name: "NumericSeconds", input: "30", expected: 30 * time.Second}, + {name: "Invalid", input: "not-a-duration", expected: 0}, + } + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + flags := flag.NewFlagSet("test", flag.ContinueOnError) + flags.String("interval", "", "doc") + app := cli.NewApp() + ctx := cli.NewContext(app, flags, nil) + _ = ctx.Set("interval", tc.input) + + target := &durationTarget{} + err := ApplyCliContext(target, ctx) + + assert.NoError(t, err) + assert.Equal(t, tc.expected, target.Interval) + }) + } +} diff --git a/internal/config/cli_flag.go b/internal/config/cli_flag.go index 8816caa40..3a92b2cb1 100644 --- a/internal/config/cli_flag.go +++ b/internal/config/cli_flag.go @@ -99,15 +99,16 @@ func (f CliFlag) CommandFlag() string { // Usage returns the command flag usage. func (f CliFlag) Usage() string { - if list.Contains(f.Tags, EnvSponsor) { + switch { + case list.Contains(f.Tags, EnvSponsor): return f.Flag.GetUsage() + " *members only*" - } else if list.Contains(f.Tags, Pro) { + case list.Contains(f.Tags, Pro): return f.Flag.GetUsage() + " *pro*" - } else if list.Contains(f.Tags, Plus) { + case list.Contains(f.Tags, Plus): return f.Flag.GetUsage() + " *plus*" - } else if list.Contains(f.Tags, Essentials) { + case list.Contains(f.Tags, Essentials): return f.Flag.GetUsage() + " *essentials*" - } else { + default: return f.Flag.GetUsage() } } diff --git a/internal/config/client_assets.go b/internal/config/client_assets.go index 4032a45d3..19cd395d6 100644 --- a/internal/config/client_assets.go +++ b/internal/config/client_assets.go @@ -38,7 +38,7 @@ func NewClientAssets(buildPath, baseUri string) *ClientAssets { // Load loads the frontend assets from a webpack manifest file. func (a *ClientAssets) Load(fileName string) error { - jsonFile, err := os.ReadFile(filepath.Join(a.BuildPath, fileName)) + jsonFile, err := os.ReadFile(filepath.Join(a.BuildPath, fileName)) //nolint:gosec // path derived from configured assets directory if err != nil { return err @@ -97,7 +97,7 @@ func (a *ClientAssets) SplashCssFile() string { // SplashCssFileContents returns the splash screen CSS file contents for embedding in HTML. func (a *ClientAssets) SplashCssFileContents() template.CSS { - return template.CSS(a.readFile(a.SplashCssFile())) + return template.CSS(a.readFile(a.SplashCssFile())) //nolint:gosec // assets are loaded from trusted local build output } // SplashJsUri returns the splash screen JS URI. @@ -121,14 +121,14 @@ func (a *ClientAssets) SplashJsFileContents() template.JS { if a.SplashJs == "" { return "" } - return template.JS(a.readFile(a.SplashJs)) + return template.JS(a.readFile(a.SplashJs)) //nolint:gosec // assets are loaded from trusted local build output } // readFile reads the file contents and returns them as string. func (a *ClientAssets) readFile(fileName string) string { if fileName == "" { return "" - } else if css, err := os.ReadFile(filepath.Join(a.BuildPath, fileName)); err != nil { + } else if css, err := os.ReadFile(filepath.Join(a.BuildPath, fileName)); err != nil { //nolint:gosec // path derived from configured assets directory return "" } else { return string(bytes.TrimSpace(css)) diff --git a/internal/config/client_config.go b/internal/config/client_config.go index a9a7b9b64..015492238 100644 --- a/internal/config/client_config.go +++ b/internal/config/client_config.go @@ -622,7 +622,7 @@ func (c *Config) ClientUser(withSettings bool) *ClientConfig { // Exclude pictures in review from total count. if c.Settings().Features.Review { - cfg.Count.All = cfg.Count.All - cfg.Count.Review + cfg.Count.All -= cfg.Count.Review } c.Db(). @@ -742,15 +742,16 @@ func (c *Config) ClientRole(role acl.Role) *ClientConfig { // ClientSession provides the client config values for the specified session. func (c *Config) ClientSession(sess *entity.Session) (cfg *ClientConfig) { - if sess.NoUser() && sess.IsClient() { + switch { + case sess.NoUser() && sess.IsClient(): cfg = c.ClientUser(false).ApplyACL(acl.Rules, sess.GetClientRole()) cfg.Settings = c.SessionSettings(sess) - } else if sess.GetUser().IsVisitor() { + case sess.GetUser().IsVisitor(): cfg = c.ClientShare() - } else if sess.GetUser().IsRegistered() { + case sess.GetUser().IsRegistered(): cfg = c.ClientUser(false).ApplyACL(acl.Rules, sess.GetUserRole()) cfg.Settings = c.SessionSettings(sess) - } else { + default: cfg = c.ClientPublic() } diff --git a/internal/config/config.go b/internal/config/config.go index efc31c33c..a563bcc0f 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -40,8 +40,8 @@ import ( "github.com/dustin/go-humanize" "github.com/jinzhu/gorm" - _ "github.com/jinzhu/gorm/dialects/mysql" - _ "github.com/jinzhu/gorm/dialects/sqlite" + _ "github.com/jinzhu/gorm/dialects/mysql" // register mysql dialect + _ "github.com/jinzhu/gorm/dialects/sqlite" // register sqlite dialect "github.com/klauspost/cpuid/v2" gc "github.com/patrickmn/go-cache" "github.com/pbnjay/memory" @@ -69,7 +69,6 @@ import ( // Config aggregates CLI flags, options.yml overrides, runtime settings, and shared resources (database, caches) for the running instance. type Config struct { - once sync.Once cliCtx *cli.Context options *Options settings *customize.Settings @@ -135,13 +134,14 @@ func initLogger() { FullTimestamp: true, }) - if Env(EnvProd) { + switch { + case Env(EnvProd): SetLogLevel(logrus.WarnLevel) - } else if Env(EnvTrace) { + case Env(EnvTrace): SetLogLevel(logrus.TraceLevel) - } else if Env(EnvDebug) { + case Env(EnvDebug): SetLogLevel(logrus.DebugLevel) - } else { + default: SetLogLevel(logrus.InfoLevel) } }) @@ -239,7 +239,7 @@ func (c *Config) Init() error { // Configure HTTPS proxy for outgoing connections. if httpsProxy := c.HttpsProxy(); httpsProxy != "" { http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{ - InsecureSkipVerify: c.HttpsProxyInsecure(), + InsecureSkipVerify: c.HttpsProxyInsecure(), //nolint:gosec // proxy settings are user-configurable and opt-in } _ = os.Setenv("HTTPS_PROXY", httpsProxy) @@ -454,7 +454,7 @@ func (c *Config) readSerial() string { backupName := c.BackupPath(serialName) if fs.FileExists(storageName) { - if data, err := os.ReadFile(storageName); err == nil && len(data) == 16 { + if data, err := os.ReadFile(storageName); err == nil && len(data) == 16 { //nolint:gosec // path is computed from config storage return string(data) } else { log.Tracef("config: could not read %s (%s)", clean.Log(storageName), err) @@ -462,7 +462,7 @@ func (c *Config) readSerial() string { } if fs.FileExists(backupName) { - if data, err := os.ReadFile(backupName); err == nil && len(data) == 16 { + if data, err := os.ReadFile(backupName); err == nil && len(data) == 16 { //nolint:gosec // backup file path is generated internally return string(data) } else { log.Tracef("config: could not read %s (%s)", clean.Log(backupName), err) @@ -729,7 +729,7 @@ func (c *Config) WakeupInterval() time.Duration { if c.options.WakeupInterval < MinWakeupInterval/time.Second { return MinWakeupInterval } else if c.options.WakeupInterval < MinWakeupInterval { - c.options.WakeupInterval = c.options.WakeupInterval * time.Second + c.options.WakeupInterval *= time.Second } // Do not run less than once per day. @@ -786,11 +786,12 @@ func (c *Config) ResolutionLimit() int { // Disabling or increasing the limit is at your own risk. // Only sponsors receive support in case of problems. - if result == 0 { + switch { + case result == 0: return DefaultResolutionLimit - } else if result < 0 { + case result < 0: return -1 - } else if result > 900 { + case result > 900: result = 900 } @@ -857,7 +858,7 @@ func (c *Config) initHub() { c.hubCancel = cancel c.hubLock.Unlock() - d := 23*time.Hour + time.Duration(float64(2*time.Hour)*rand.Float64()) + d := 23*time.Hour + time.Duration(float64(2*time.Hour)*rand.Float64()) //nolint:gosec // jitter for scheduling only, crypto not required ticker := time.NewTicker(d) go func() { diff --git a/internal/config/config_auth.go b/internal/config/config_auth.go index ca472631e..6f5242ff2 100644 --- a/internal/config/config_auth.go +++ b/internal/config/config_auth.go @@ -14,7 +14,9 @@ import ( ) const ( + // AuthModePublic disables authentication and runs the app in public mode. AuthModePublic = "public" + // AuthModePasswd enables password-based authentication (default). AuthModePasswd = "password" ) @@ -92,7 +94,7 @@ func (c *Config) AdminPassword() string { } else if fileName := FlagFilePath("ADMIN_PASSWORD"); fileName == "" { // No password set, this is not an error. return "" - } else if b, err := os.ReadFile(fileName); err != nil || len(b) == 0 { + } else if b, err := os.ReadFile(fileName); err != nil || len(b) == 0 { //nolint:gosec // path is derived from config directory log.Warnf("config: failed to read admin password from %s (%s)", fileName, err) return "" } else { @@ -111,11 +113,12 @@ func (c *Config) AdminScope() string { // PasswordLength returns the minimum password length in characters. func (c *Config) PasswordLength() int { - if c.Public() { + switch { + case c.Public(): return 0 - } else if c.options.PasswordLength < 1 { + case c.options.PasswordLength < 1: return entity.PasswordLengthDefault - } else if c.options.PasswordLength > txt.ClipPassword { + case c.options.PasswordLength > txt.ClipPassword: return txt.ClipPassword } @@ -194,11 +197,12 @@ func (c *Config) SessionTimeout() int64 { // SessionCache returns the default session cache duration in seconds. func (c *Config) SessionCache() int64 { - if c.options.SessionCache == 0 { + switch { + case c.options.SessionCache == 0: return DefaultSessionCache - } else if c.options.SessionCache < 60 { + case c.options.SessionCache < 60: return 60 - } else if c.options.SessionCache > 3600 { + case c.options.SessionCache > 3600: return 3600 } diff --git a/internal/config/config_backup.go b/internal/config/config_backup.go index b04331b82..b8a49d898 100644 --- a/internal/config/config_backup.go +++ b/internal/config/config_backup.go @@ -8,8 +8,10 @@ import ( ) const ( + // DefaultBackupSchedule defines the default cron schedule for backups. DefaultBackupSchedule = "daily" - DefaultBackupRetain = 3 + // DefaultBackupRetain sets how many backup sets are kept by default. + DefaultBackupRetain = 3 ) // DisableBackups checks if database and album backups as well as YAML sidecar files should not be created. diff --git a/internal/config/config_cache.go b/internal/config/config_cache.go index ca6eea2d8..3e5c344b9 100644 --- a/internal/config/config_cache.go +++ b/internal/config/config_cache.go @@ -6,10 +6,13 @@ import ( gc "github.com/patrickmn/go-cache" ) +// Cache stores shared config values for quick reuse across requests. var Cache = gc.New(time.Hour, 15*time.Minute) const ( - CacheKeyAppManifest = "app-manifest" + // CacheKeyAppManifest is the cache key for the PWA manifest. + CacheKeyAppManifest = "app-manifest" + // CacheKeyWallpaperUri is the cache key for the current wallpaper URI. CacheKeyWallpaperUri = "wallpaper-uri" ) diff --git a/internal/config/config_cluster.go b/internal/config/config_cluster.go index e70af155e..7a6bd2456 100644 --- a/internal/config/config_cluster.go +++ b/internal/config/config_cluster.go @@ -22,7 +22,11 @@ import ( // DefaultPortalUrl specifies the default portal URL with variable cluster domain. var DefaultPortalUrl = "https://portal.${PHOTOPRISM_CLUSTER_DOMAIN}" + +// DefaultNodeRole is the default node role assigned when none is configured. var DefaultNodeRole = cluster.RoleApp + +// DefaultJWTAllowedScopes lists default OAuth scopes for cluster-issued JWTs. var DefaultJWTAllowedScopes = "config cluster vision metrics" // ClusterDomain returns the cluster DOMAIN (lowercase DNS name; 1–63 chars). @@ -146,7 +150,7 @@ func (c *Config) JoinToken() string { } if fs.FileExistsNotEmpty(fileName) { - if b, err := os.ReadFile(fileName); err != nil || len(b) == 0 { + if b, err := os.ReadFile(fileName); err != nil || len(b) == 0 { //nolint:gosec // path derived from config directory log.Warnf("config: could not read cluster join token from %s (%s)", fileName, err) } else if s := strings.TrimSpace(string(b)); rnd.IsJoinToken(s, false) { if c.cache != nil { @@ -355,7 +359,7 @@ func (c *Config) NodeClientSecret() string { return "" } - if b, err := os.ReadFile(fileName); err == nil && len(b) > 0 { + if b, err := os.ReadFile(fileName); err == nil && len(b) > 0 { //nolint:gosec // path derived from config directory // Do not cache the value. Always read from the disk to ensure // that updates from other processes are observed. return string(b) @@ -530,7 +534,7 @@ func (c *Config) SaveClusterUUID(uuid string) error { var m Values if fs.FileExists(fileName) { - if b, err := os.ReadFile(fileName); err == nil && len(b) > 0 { + if b, err := os.ReadFile(fileName); err == nil && len(b) > 0 { //nolint:gosec // path derived from config directory _ = yaml.Unmarshal(b, &m) } } @@ -573,7 +577,7 @@ func (c *Config) SaveNodeUUID(uuid string) error { var m Values if fs.FileExists(fileName) { - if b, err := os.ReadFile(fileName); err == nil && len(b) > 0 { + if b, err := os.ReadFile(fileName); err == nil && len(b) > 0 { //nolint:gosec // path derived from config directory _ = yaml.Unmarshal(b, &m) } } diff --git a/internal/config/config_cluster_test.go b/internal/config/config_cluster_test.go index 2592742ab..ba5db7015 100644 --- a/internal/config/config_cluster_test.go +++ b/internal/config/config_cluster_test.go @@ -297,7 +297,7 @@ func TestConfig_Cluster(t *testing.T) { assert.True(t, rnd.IsJoinToken(token, false)) assert.FileExists(t, tokenFile) - data, readErr := os.ReadFile(tokenFile) + data, readErr := os.ReadFile(tokenFile) //nolint:gosec // test reads file from temp directory assert.NoError(t, readErr) assert.Equal(t, token, strings.TrimSpace(string(data))) }) @@ -311,7 +311,7 @@ func TestConfig_Cluster(t *testing.T) { assert.NoError(t, err) assert.FileExists(t, fileName) - data, readErr := os.ReadFile(fileName) + data, readErr := os.ReadFile(fileName) //nolint:gosec // test reads file from temp directory assert.NoError(t, readErr) assert.Equal(t, cluster.ExampleClientSecret, strings.TrimSpace(string(data))) }) @@ -349,7 +349,7 @@ func TestConfig_Cluster(t *testing.T) { secretDir := filepath.Join(c.NodeConfigPath(), fs.SecretsDir) assert.NoError(t, os.MkdirAll(secretDir, fs.ModeDir)) - assert.NoError(t, os.Chmod(secretDir, 0o500)) + assert.NoError(t, os.Chmod(secretDir, 0o500)) //nolint:gosec // making directory intentionally non-writable for fallback test _, err := c.SaveNodeClientSecret(cluster.ExampleClientSecret) assert.Error(t, err) @@ -385,7 +385,7 @@ func TestConfig_Cluster(t *testing.T) { secretDir := filepath.Join(c.NodeConfigPath(), fs.SecretsDir) assert.NoError(t, os.MkdirAll(secretDir, fs.ModeDir)) - assert.NoError(t, os.Chmod(secretDir, 0o500)) + assert.NoError(t, os.Chmod(secretDir, 0o500)) //nolint:gosec // making directory intentionally non-writable for fallback test _, _, err := c.SaveJoinToken("") assert.Error(t, err) @@ -606,7 +606,7 @@ func TestConfig_ClusterUUID_GenerateAndPersist(t *testing.T) { } // Verify content persisted to options.yml. - b, err := os.ReadFile(optionsYaml) + b, err := os.ReadFile(optionsYaml) //nolint:gosec // test reads generated options file assert.NoError(t, err) var m map[string]any assert.NoError(t, yaml.Unmarshal(b, &m)) diff --git a/internal/config/config_const.go b/internal/config/config_const.go index 83521dd39..7bd6f118b 100644 --- a/internal/config/config_const.go +++ b/internal/config/config_const.go @@ -27,16 +27,18 @@ const ThemeUri = "/_theme" // DefaultIndexSchedule defines the default indexing schedule in cron format. const DefaultIndexSchedule = "" // e.g. "0 */3 * * *" for every 3 hours -// DefaultAutoIndexDelay and DefaultAutoImportDelay set the default safety delay duration -// before starting to index/import in the background. +// DefaultAutoIndexDelay sets the default delay (in seconds) before background indexing starts. const DefaultAutoIndexDelay = 300 // 5 Minutes +// DefaultAutoImportDelay sets the default delay (in seconds) before background imports start (-1 disables). const DefaultAutoImportDelay = -1 // Disabled -// MinWakeupInterval and MaxWakeupInterval limit the interval duration -// in which the background worker can be invoked. -const MinWakeupInterval = time.Minute // 1 Minute -const MaxWakeupInterval = time.Hour * 24 // 1 Day +// MinWakeupInterval is the minimum allowed interval for the background worker. +const MinWakeupInterval = time.Minute // 1 Minute +// MaxWakeupInterval is the maximum allowed interval for the background worker. +const MaxWakeupInterval = time.Hour * 24 // 1 Day +// DefaultWakeupIntervalSeconds is the default worker interval in seconds. const DefaultWakeupIntervalSeconds = int(15 * 60) // 15 Minutes +// DefaultWakeupInterval is the default worker interval as a duration. const DefaultWakeupInterval = time.Second * time.Duration(DefaultWakeupIntervalSeconds) // MegaByte defines a megabyte in bytes. diff --git a/internal/config/config_customize.go b/internal/config/config_customize.go index ea5012076..dabcf8bb7 100644 --- a/internal/config/config_customize.go +++ b/internal/config/config_customize.go @@ -72,12 +72,15 @@ func (c *Config) WallpaperUri() string { // Complete URI as needed if file path is valid. if fileName := clean.Path(wallpaperUri); fileName == "" { return "" - } else if fs.FileExists(path.Join(c.StaticPath(), wallpaperPath, fileName)) { - wallpaperUri = c.StaticAssetUri(path.Join(wallpaperPath, fileName)) - } else if fs.FileExists(c.CustomStaticFile(path.Join(wallpaperPath, fileName))) { - wallpaperUri = c.CustomStaticAssetUri(path.Join(wallpaperPath, fileName)) } else { - return "" + switch { + case fs.FileExists(path.Join(c.StaticPath(), wallpaperPath, fileName)): + wallpaperUri = c.StaticAssetUri(path.Join(wallpaperPath, fileName)) + case fs.FileExists(c.CustomStaticFile(path.Join(wallpaperPath, fileName))): + wallpaperUri = c.CustomStaticAssetUri(path.Join(wallpaperPath, fileName)) + default: + return "" + } } // Cache wallpaper URI if not empty. diff --git a/internal/config/config_db.go b/internal/config/config_db.go index bb46ed8a6..3f8698d15 100644 --- a/internal/config/config_db.go +++ b/internal/config/config_db.go @@ -11,7 +11,7 @@ import ( "time" "github.com/jinzhu/gorm" - _ "github.com/jinzhu/gorm/dialects/mysql" + _ "github.com/jinzhu/gorm/dialects/mysql" // register mysql dialect _ "github.com/jinzhu/gorm/dialects/sqlite" "golang.org/x/mod/semver" @@ -298,7 +298,7 @@ func (c *Config) DatabasePassword() string { } else if fileName := FlagFilePath("DATABASE_PASSWORD"); fileName == "" { // No password set, this is not an error. return "" - } else if b, err := os.ReadFile(fileName); err != nil || len(b) == 0 { + } else if b, err := os.ReadFile(fileName); err != nil || len(b) == 0 { //nolint:gosec // path derived from environment variable for DB password log.Warnf("config: failed to read database password from %s (%s)", fileName, err) return "" } else { @@ -536,11 +536,12 @@ func (c *Config) checkDb(db *gorm.DB) error { c.dbVersion = clean.Version(res.Value) - if c.dbVersion == "" { + switch { + case c.dbVersion == "": log.Warnf("config: unknown database server version") - } else if !c.IsDatabaseVersion("v10.0.0") { + case !c.IsDatabaseVersion("v10.0.0"): return fmt.Errorf("config: MySQL %s is not supported, see https://docs.photoprism.app/getting-started/#databases", c.dbVersion) - } else if !c.IsDatabaseVersion("v10.5.12") { + case !c.IsDatabaseVersion("v10.5.12"): return fmt.Errorf("config: MariaDB %s is not supported, see https://docs.photoprism.app/getting-started/#databases", c.dbVersion) } case SQLite3: @@ -641,7 +642,7 @@ func (c *Config) connectDb() error { // ImportSQL imports a file to the currently configured database. func (c *Config) ImportSQL(filename string) { - contents, err := os.ReadFile(filename) + contents, err := os.ReadFile(filename) //nolint:gosec // import path is provided by trusted caller if err != nil { log.Error(err) diff --git a/internal/config/config_faces.go b/internal/config/config_faces.go index ee0d8c7ad..75ce7d294 100644 --- a/internal/config/config_faces.go +++ b/internal/config/config_faces.go @@ -11,7 +11,7 @@ import ( ) // FaceEngine returns the configured face detection engine. When the config is -// nil or the vision subsystem is not initialised it reports `face.EngineNone` +// nil or the vision subsystem is not initialized it reports `face.EngineNone` // so callers can short-circuit gracefully. func (c *Config) FaceEngine() string { if c == nil { diff --git a/internal/config/config_faces_test.go b/internal/config/config_faces_test.go index 1d5937dd0..809da49e8 100644 --- a/internal/config/config_faces_test.go +++ b/internal/config/config_faces_test.go @@ -37,9 +37,9 @@ func TestConfig_FaceEngine(t *testing.T) { c.options.ModelsPath = tempModels modelDir := filepath.Join(tempModels, "scrfd") - require.NoError(t, os.MkdirAll(modelDir, 0o755)) + require.NoError(t, os.MkdirAll(modelDir, 0o750)) modelFile := filepath.Join(modelDir, face.DefaultONNXModelFilename) - require.NoError(t, os.WriteFile(modelFile, []byte("onnx"), 0o644)) + require.NoError(t, os.WriteFile(modelFile, []byte("onnx"), 0o600)) c.options.FaceEngine = face.EngineAuto assert.Equal(t, face.EngineONNX, c.FaceEngine()) diff --git a/internal/config/config_features.go b/internal/config/config_features.go index f0f33ee1a..df9cb235c 100644 --- a/internal/config/config_features.go +++ b/internal/config/config_features.go @@ -6,7 +6,10 @@ import ( "github.com/photoprism/photoprism/internal/service/hub/places" ) +// Sponsor indicates whether sponsor or demo features are enabled. var Sponsor = Env(EnvDemo, EnvSponsor, EnvTest) + +// Features represents the current feature tier (community by default). var Features = Community // DisableFrontend checks if the web user interface routes should be disabled. diff --git a/internal/config/config_features_test.go b/internal/config/config_features_test.go index 8882a2918..dd1eae830 100644 --- a/internal/config/config_features_test.go +++ b/internal/config/config_features_test.go @@ -212,8 +212,3 @@ func withVisionConfig(t *testing.T, cfg *vision.ConfigValues) { vision.Config = prev }) } - -func cloneVisionModel(m *vision.Model) *vision.Model { - copy := *m - return © -} diff --git a/internal/config/config_ffmpeg.go b/internal/config/config_ffmpeg.go index 1c0edfb3a..70d342a6b 100644 --- a/internal/config/config_ffmpeg.go +++ b/internal/config/config_ffmpeg.go @@ -105,15 +105,16 @@ func (c *Config) FFmpegPreset() string { // FFmpegDevice returns the ffmpeg device path for supported hardware encoders (optional). func (c *Config) FFmpegDevice() string { - if c.options.FFmpegDevice == "" { + switch { + case c.options.FFmpegDevice == "": return "" - } else if txt.IsUInt(c.options.FFmpegDevice) { + case txt.IsUInt(c.options.FFmpegDevice): return c.options.FFmpegDevice - } else if fs.DeviceExists(c.options.FFmpegDevice) { + case fs.DeviceExists(c.options.FFmpegDevice): return c.options.FFmpegDevice + default: + return "" } - - return "" } // FFmpegMapVideo returns the video streams to be transcoded as string. @@ -140,13 +141,14 @@ func (c *Config) FFmpegOptions(encoder encode.Encoder, bitrate string) (encode.O opt := encode.NewVideoOptions(c.FFmpegBin(), encoder, c.FFmpegSize(), c.FFmpegQuality(), c.FFmpegPreset(), c.FFmpegDevice(), c.FFmpegMapVideo(), c.FFmpegMapAudio()) // Check options and return error if invalid. - if opt.Bin == "" { + switch { + case opt.Bin == "": return opt, fmt.Errorf("ffmpeg is not installed") - } else if c.DisableFFmpeg() { + case c.DisableFFmpeg(): return opt, fmt.Errorf("ffmpeg is disabled") - } else if bitrate == "" { + case bitrate == "": return opt, fmt.Errorf("bitrate must not be empty") - } else if encoder.String() == "" { + case encoder.String() == "": return opt, fmt.Errorf("encoder must not be empty") } diff --git a/internal/config/config_oidc.go b/internal/config/config_oidc.go index 67b9bfd9a..570f88084 100644 --- a/internal/config/config_oidc.go +++ b/internal/config/config_oidc.go @@ -16,20 +16,25 @@ import ( ) const ( + // OidcDefaultProviderName is the default display name for the built-in OIDC provider. OidcDefaultProviderName = "OpenID" + // OidcDefaultProviderIcon is the default icon path for the built-in OIDC provider. OidcDefaultProviderIcon = "img/oidc.svg" - OidcLoginUri = ApiUri + "/oidc/login" - OidcRedirectUri = ApiUri + "/oidc/redirect" + // OidcLoginUri is the login endpoint path for OIDC. + OidcLoginUri = ApiUri + "/oidc/login" + // OidcRedirectUri is the callback endpoint path for OIDC. + OidcRedirectUri = ApiUri + "/oidc/redirect" ) // OIDCEnabled checks if sign-on via OpenID Connect (OIDC) is fully configured and enabled. func (c *Config) OIDCEnabled() bool { - if c.options.DisableOIDC { + switch { + case c.options.DisableOIDC: return false - } else if !c.SiteHttps() { + case !c.SiteHttps(): // Site URL must start with "https://". return false - } else if !strings.HasPrefix(c.options.OIDCUri, "https://") { + case !strings.HasPrefix(c.options.OIDCUri, "https://"): // OIDC provider URI must start with "https://". return false } @@ -65,7 +70,7 @@ func (c *Config) OIDCSecret() string { } else if fileName := FlagFilePath("OIDC_SECRET"); fileName == "" { // No secret set, this is not an error. return "" - } else if b, err := os.ReadFile(fileName); err != nil || len(b) == 0 { + } else if b, err := os.ReadFile(fileName); err != nil || len(b) == 0 { //nolint:gosec // path derived from config directory log.Warnf("config: failed to read OIDC client secret from %s (%s)", fileName, err) return "" } else { diff --git a/internal/config/config_server.go b/internal/config/config_server.go index 0def51873..67d5d6fb6 100644 --- a/internal/config/config_server.go +++ b/internal/config/config_server.go @@ -14,7 +14,9 @@ import ( ) const ( - HttpModeProd = "release" + // HttpModeProd selects Gin's release mode. + HttpModeProd = "release" + // HttpModeDebug selects Gin's debug mode. HttpModeDebug = "debug" ) @@ -237,9 +239,7 @@ func (c *Config) TemplateFiles() []string { continue } - for _, tmplName := range matches { - results = append(results, tmplName) - } + results = append(results, matches...) } return results diff --git a/internal/config/config_site.go b/internal/config/config_site.go index cd3f0bd46..01ada875c 100644 --- a/internal/config/config_site.go +++ b/internal/config/config_site.go @@ -199,7 +199,7 @@ func (c *Config) RobotsTxt() ([]byte, error) { } else if fileName := filepath.Join(c.ConfigPath(), "robots.txt"); !fs.FileExists(fileName) { // Do not allow indexing if config/robots.txt does not exist. return robotsTxt, nil - } else if robots, robotsErr := os.ReadFile(fileName); robotsErr != nil { + } else if robots, robotsErr := os.ReadFile(fileName); robotsErr != nil { //nolint:gosec // robots file path derived from config directory // Log error and do not allow indexing if config/robots.txt cannot be read. log.Debugf("config: failed to read robots.txt file (%s)", clean.Error(robotsErr)) return robotsTxt, robotsErr diff --git a/internal/config/config_thumb.go b/internal/config/config_thumb.go index f8e628818..52ca4956b 100644 --- a/internal/config/config_thumb.go +++ b/internal/config/config_thumb.go @@ -52,12 +52,13 @@ func (c *Config) ThumbLibrary() string { default: c.options.ThumbLibrary = clean.TypeLowerUnderscore(c.options.ThumbLibrary) - if c.options.ThumbLibrary == "imagine" || c.options.ThumbLibrary == "" { + switch c.options.ThumbLibrary { + case "imagine", "": c.options.ThumbLibrary = thumb.LibImaging return thumb.LibImaging - } else if c.options.ThumbLibrary == "vips" || c.options.ThumbLibrary == "libvips" { + case "vips", "libvips": c.options.ThumbLibrary = thumb.LibVips - } else { + default: c.options.ThumbLibrary = thumb.LibAuto } diff --git a/internal/config/config_tls.go b/internal/config/config_tls.go index d354e59d5..cb7ba5472 100644 --- a/internal/config/config_tls.go +++ b/internal/config/config_tls.go @@ -8,7 +8,9 @@ import ( ) const ( + // PrivateKeyExt is the default file extension for TLS private keys. PrivateKeyExt = ".key" + // PublicCertExt is the default file extension for TLS certificates. PublicCertExt = ".crt" ) diff --git a/internal/config/config_usage_test.go b/internal/config/config_usage_test.go index 6e4b2ec01..b667c9561 100644 --- a/internal/config/config_usage_test.go +++ b/internal/config/config_usage_test.go @@ -25,7 +25,7 @@ func TestConfig_Usage(t *testing.T) { t.Logf("Storage Used: %d MB (%d%%), Free: %d MB (%d%%), Total %d MB", result2.FilesUsed/duf.MB, result2.FilesUsedPct, result2.FilesFree/duf.MB, result2.FilesFreePct, result2.FilesTotal/duf.MB) - //result cached + // result cached assert.GreaterOrEqual(t, result2.FilesUsed, uint64(60000000)) assert.GreaterOrEqual(t, result2.FilesTotal, uint64(60000000)) diff --git a/internal/config/config_vision.go b/internal/config/config_vision.go index 0c1629976..2af2a23fe 100644 --- a/internal/config/config_vision.go +++ b/internal/config/config_vision.go @@ -104,7 +104,7 @@ func (c *Config) VisionKey() string { } else if fileName := FlagFilePath("VISION_KEY"); fileName == "" { // No access token set, this is not an error. return "" - } else if b, err := os.ReadFile(fileName); err != nil || len(b) == 0 { + } else if b, err := os.ReadFile(fileName); err != nil || len(b) == 0 { //nolint:gosec // path derived from config directory log.Warnf("config: failed to read vision key from %s (%s)", fileName, err) return "" } else { diff --git a/internal/config/develop.go b/internal/config/develop.go index e81364d66..08d593edf 100644 --- a/internal/config/develop.go +++ b/internal/config/develop.go @@ -1,5 +1,4 @@ //go:build develop -// +build develop package config diff --git a/internal/config/err.go b/internal/config/err.go index 6a415fc9f..75b825b3c 100644 --- a/internal/config/err.go +++ b/internal/config/err.go @@ -5,9 +5,11 @@ import ( ) var ( + // ErrReadOnly indicates an action is not permitted in read-only mode. ErrReadOnly = errors.New("not available in read-only mode") ) +// LogErr logs a config-related error if it is non-nil. func LogErr(err error) { if err != nil { log.Errorf("config: %s", err.Error()) diff --git a/internal/config/extensions_test.go b/internal/config/extensions_test.go index 17e26e773..ae903d8ab 100644 --- a/internal/config/extensions_test.go +++ b/internal/config/extensions_test.go @@ -23,9 +23,6 @@ func resetExtensionsForTest(t *testing.T) func() { bootExtensions.Store(Extensions{}) bootMutex.Unlock() - originalExtOnce := extInit - originalBootOnce := bootInit - extInit = sync.Once{} bootInit = sync.Once{} @@ -38,8 +35,8 @@ func resetExtensionsForTest(t *testing.T) func() { bootExtensions.Store(savedBoot) bootMutex.Unlock() - extInit = originalExtOnce - bootInit = originalBootOnce + extInit = sync.Once{} + bootInit = sync.Once{} } } diff --git a/internal/config/messages.go b/internal/config/messages.go index f4875923f..cb6d08b9a 100644 --- a/internal/config/messages.go +++ b/internal/config/messages.go @@ -1,8 +1,12 @@ package config var ( - SignUpURL = "https://www.photoprism.app/membership" + // SignUpURL points to the membership sign-up page. + SignUpURL = "https://www.photoprism.app/membership" + // MsgSponsor is the default sponsorship message shown to users. MsgSponsor = "Become a member today, support our mission and enjoy our member benefits! 💎" - MsgSignUp = "Visit " + SignUpURL + " to learn more." - SignUp = Values{"message": MsgSponsor, "url": SignUpURL} + // MsgSignUp is the default sign-up helper text. + MsgSignUp = "Visit " + SignUpURL + " to learn more." + // SignUp bundles the sign-up message and URL used in client responses. + SignUp = Values{"message": MsgSponsor, "url": SignUpURL} ) diff --git a/internal/config/options.go b/internal/config/options.go index 48ca5bee0..87e6cb048 100644 --- a/internal/config/options.go +++ b/internal/config/options.go @@ -1,7 +1,6 @@ package config import ( - "errors" "fmt" "net/url" "os" @@ -333,10 +332,10 @@ func (o *Options) Load(fileName string) error { } if !fs.FileExists(fileName) { - return errors.New(fmt.Sprintf("%s not found", fileName)) + return fmt.Errorf("%s not found", fileName) } - yamlConfig, err := os.ReadFile(fileName) + yamlConfig, err := os.ReadFile(fileName) //nolint:gosec // configuration file path provided by user/config if err != nil { return err diff --git a/internal/config/pwa/icon.go b/internal/config/pwa/icon.go index c9e5b10a1..f550154e5 100644 --- a/internal/config/pwa/icon.go +++ b/internal/config/pwa/icon.go @@ -28,9 +28,10 @@ func NewIcons(c Config) Icons { staticUri := c.StaticUri appIcon := c.Icon - if appIcon == "" { + switch { + case appIcon == "": appIcon = "logo" - } else if c.ThemePath != "" && strings.HasPrefix(appIcon, c.ThemeUri) { + case c.ThemePath != "" && strings.HasPrefix(appIcon, c.ThemeUri): var appIconSize string var appIconType string @@ -52,7 +53,7 @@ func NewIcons(c Config) Icons { Sizes: appIconSize, Type: appIconType, }} - } else if strings.Contains(appIcon, "/") { + case strings.Contains(appIcon, "/"): var appIconType string switch fs.FileType(filepath.Base(appIcon)) { diff --git a/internal/config/report.go b/internal/config/report.go index 8219fa759..e36c05a9b 100644 --- a/internal/config/report.go +++ b/internal/config/report.go @@ -20,7 +20,7 @@ func (c *Config) Report() (rows [][]string, cols []string) { rows = [][]string{ // Authentication. - {"auth-mode", fmt.Sprintf("%s", c.AuthMode())}, + {"auth-mode", c.AuthMode()}, {"admin-user", c.AdminUser()}, {"admin-password", strings.Repeat("*", utf8.RuneCountInString(c.AdminPassword()))}, {"admin-scope", c.AdminScope()}, @@ -182,12 +182,12 @@ func (c *Config) Report() (rows [][]string, cols []string) { {"portal-url", c.PortalUrl()}, {"portal-config-path", c.PortalConfigPath()}, {"portal-theme-path", c.PortalThemePath()}, - {"join-token", fmt.Sprintf("%s", strings.Repeat("*", utf8.RuneCountInString(c.JoinToken())))}, + {"join-token", strings.Repeat("*", utf8.RuneCountInString(c.JoinToken()))}, {"node-name", c.NodeName()}, {"node-role", c.NodeRole()}, {"node-uuid", c.NodeUUID()}, {"node-client-id", c.NodeClientID()}, - {"node-client-secret", fmt.Sprintf("%s", strings.Repeat("*", utf8.RuneCountInString(c.NodeClientSecret())))}, + {"node-client-secret", strings.Repeat("*", utf8.RuneCountInString(c.NodeClientSecret()))}, {"jwks-url", c.JWKSUrl()}, {"jwks-cache-ttl", fmt.Sprintf("%d", c.JWKSCacheTTL())}, {"jwt-scope", c.JWTAllowedScopes().String()}, @@ -308,19 +308,20 @@ func (c *Config) Report() (rows [][]string, cols []string) { {"face-engine-run", vision.ReportRunType(c.FaceEngineRunType())}, }...) - if faceEngine == face.EngineONNX { + switch faceEngine { + case face.EngineONNX: rows = append(rows, [][]string{ {"face-engine-threads", fmt.Sprintf("%d", c.FaceEngineThreads())}, {"face-size", fmt.Sprintf("%d", c.FaceSize())}, {"face-score", fmt.Sprintf("%f", c.FaceScore())}, }...) - } else if faceEngine == face.EnginePigo { + case face.EnginePigo: rows = append(rows, [][]string{ {"face-size", fmt.Sprintf("%d", c.FaceSize())}, {"face-score", fmt.Sprintf("%f", c.FaceScore())}, {"face-angle", fmt.Sprintf("%v", c.FaceAngles())}, }...) - } else { + default: rows = append(rows, [][]string{ {"face-engine-threads", fmt.Sprintf("%d", c.FaceEngineThreads())}, {"face-size", fmt.Sprintf("%d", c.FaceSize())}, diff --git a/internal/config/schedule.go b/internal/config/schedule.go index 8d4623b16..8b6ddfd35 100644 --- a/internal/config/schedule.go +++ b/internal/config/schedule.go @@ -11,7 +11,9 @@ import ( ) const ( - ScheduleDaily = "daily" + // ScheduleDaily represents the keyword for daily schedules. + ScheduleDaily = "daily" + // ScheduleWeekly represents the keyword for weekly schedules. ScheduleWeekly = "weekly" ) @@ -26,9 +28,9 @@ func Schedule(s string) string { switch s { case ScheduleDaily: - return fmt.Sprintf("%d %d * * *", rand.IntN(60), rand.IntN(24)) + return fmt.Sprintf("%d %d * * *", rand.IntN(60), rand.IntN(24)) //nolint:gosec // non-cryptographic randomness for scheduling case ScheduleWeekly: - return fmt.Sprintf("%d %d * * 0", rand.IntN(60), rand.IntN(24)) + return fmt.Sprintf("%d %d * * 0", rand.IntN(60), rand.IntN(24)) //nolint:gosec // non-cryptographic randomness for scheduling } // Example: "0 12 * * *" stands for daily at noon. diff --git a/internal/config/test.go b/internal/config/test.go index 8ac86a8c7..dd8d864f3 100644 --- a/internal/config/test.go +++ b/internal/config/test.go @@ -14,7 +14,7 @@ import ( gc "github.com/patrickmn/go-cache" "github.com/urfave/cli/v2" - _ "github.com/jinzhu/gorm/dialects/mysql" + _ "github.com/jinzhu/gorm/dialects/mysql" // register mysql dialect _ "github.com/jinzhu/gorm/dialects/sqlite" "github.com/photoprism/photoprism/internal/config/customize" @@ -50,7 +50,7 @@ func testDataPath(assetsPath string) string { // PkgNameRegexp normalizes database file names by stripping unsupported // characters from the Go package identifier supplied by tests. -var PkgNameRegexp = regexp.MustCompile("[^a-zA-Z\\-_]+") +var PkgNameRegexp = regexp.MustCompile(`[^a-zA-Z\-_]+`) // NewTestOptions builds fully-populated Options suited for backend tests. It // creates an isolated storage directory under storage/testdata (or the @@ -329,17 +329,17 @@ func NewTestConfig(dbName string) *Config { s := customize.NewSettings(c.DefaultTheme(), c.DefaultLocale(), c.DefaultTimezone().String()) if err := fs.MkdirAll(c.ConfigPath()); err != nil { - log.Fatalf("config: %s", err.Error()) + log.Panicf("config: %s", err.Error()) } // Save settings next to the test config path, reusing any existing // `.yaml`/`.yml` variant so the tests mirror production behavior. if err := s.Save(fs.ConfigFilePath(c.ConfigPath(), "settings", fs.ExtYml)); err != nil { - log.Fatalf("config: %s", err.Error()) + log.Panicf("config: %s", err.Error()) } if err := c.Init(); err != nil { - log.Fatalf("config: %s", err.Error()) + log.Panicf("config: %s", err.Error()) } if err := c.InitializeTestData(); err != nil { diff --git a/internal/config/ttl/cache.go b/internal/config/ttl/cache.go index f22036819..c636c5587 100644 --- a/internal/config/ttl/cache.go +++ b/internal/config/ttl/cache.go @@ -1,8 +1,12 @@ package ttl var ( - CacheMaxAge Duration = 31536000 // 365 days is the maximum cache time - CacheDefault Duration = 2592000 // 30 days is the default cache time - CacheVideo Duration = 21600 // 6 hours for video streams - CacheCover Duration = 3600 // 1 hour for album cover images + // CacheMaxAge is the maximum cache duration (in seconds). + CacheMaxAge Duration = 31536000 // 365 days is the maximum cache time + // CacheDefault is the default cache duration (in seconds). + CacheDefault Duration = 2592000 // 30 days is the default cache time + // CacheVideo is the cache duration for video streams (in seconds). + CacheVideo Duration = 21600 // 6 hours for video streams + // CacheCover is the cache duration for album cover images (in seconds). + CacheCover Duration = 3600 // 1 hour for album cover images ) diff --git a/internal/config/var.go b/internal/config/var.go index 3d7eae5f4..a0442c3d3 100644 --- a/internal/config/var.go +++ b/internal/config/var.go @@ -7,6 +7,8 @@ import ( var ( once sync.Once initThumbsMutex sync.Mutex - LowMem = false - TotalMem uint64 + // LowMem indicates the system has less RAM than the recommended minimum. + LowMem = false + // TotalMem stores the detected system memory in bytes. + TotalMem uint64 ) From 6630a5a07c798f08cc329e735f0ada63e124a793 Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sun, 23 Nov 2025 08:47:17 +0100 Subject: [PATCH 041/195] CI: Apply Go linter recommendations to "internal/thumb/..." #5330 Signed-off-by: Michael Mayer --- internal/thumb/avatar/avatar.go | 12 ++++++------ internal/thumb/avatar/download_test.go | 2 +- internal/thumb/crop/errors.go | 1 + internal/thumb/crop/image.go | 11 ++++++----- internal/thumb/crop/request.go | 2 +- internal/thumb/crop/sizes.go | 3 +++ internal/thumb/frame/angle.go | 2 +- internal/thumb/frame/frame.go | 2 ++ internal/thumb/frame/point.go | 4 ++-- 9 files changed, 23 insertions(+), 16 deletions(-) diff --git a/internal/thumb/avatar/avatar.go b/internal/thumb/avatar/avatar.go index 0d390daa2..0f17e5582 100644 --- a/internal/thumb/avatar/avatar.go +++ b/internal/thumb/avatar/avatar.go @@ -25,13 +25,13 @@ Additional information can be found in our Developer Guide: package avatar import ( - _ "image/gif" - _ "image/jpeg" - _ "image/png" + _ "image/gif" // register gif decoder + _ "image/jpeg" // register jpeg decoder + _ "image/png" // register png decoder - _ "golang.org/x/image/bmp" - _ "golang.org/x/image/tiff" - _ "golang.org/x/image/webp" + _ "golang.org/x/image/bmp" // register bmp decoder + _ "golang.org/x/image/tiff" // register tiff decoder + _ "golang.org/x/image/webp" // register webp decoder "github.com/photoprism/photoprism/internal/event" ) diff --git a/internal/thumb/avatar/download_test.go b/internal/thumb/avatar/download_test.go index 0223e2cde..22df4b29e 100644 --- a/internal/thumb/avatar/download_test.go +++ b/internal/thumb/avatar/download_test.go @@ -63,7 +63,7 @@ func TestSafeDownload_Succeeds(t *testing.T) { if err := SafeDownload(dest, ts.URL, &safe.Options{Timeout: 5 * time.Second, MaxSizeBytes: 1 << 20, AllowPrivate: true}); err != nil { t.Fatalf("unexpected error: %v", err) } - b, err := os.ReadFile(dest) + b, err := os.ReadFile(dest) //nolint:gosec // test reads temp file if err != nil { t.Fatalf("read: %v", err) } diff --git a/internal/thumb/crop/errors.go b/internal/thumb/crop/errors.go index 66d9c9f88..593915d19 100644 --- a/internal/thumb/crop/errors.go +++ b/internal/thumb/crop/errors.go @@ -5,5 +5,6 @@ import ( ) var ( + // ErrNotFound indicates the requested crop size or option was not found. ErrNotFound = errors.New("not found") ) diff --git a/internal/thumb/crop/image.go b/internal/thumb/crop/image.go index e1dfc4ab0..3601c1aa5 100644 --- a/internal/thumb/crop/image.go +++ b/internal/thumb/crop/image.go @@ -154,12 +154,13 @@ func findIdealThumbFileName(hash string, width int, filePath string) (fileName s // Resolve symlinks. name, err := fs.Resolve(filepath.Join(filePath, fmt.Sprintf(thumbFileNames[i], hash))) - if err != nil || !fs.FileExists(name) { + switch { + case err != nil || !fs.FileExists(name): continue - } else if s.Width < width { + case s.Width < width: fileName = name continue - } else { + default: return name } } @@ -176,7 +177,7 @@ func openIdealThumbFile(fileName, hash string, area Area, size Size) (result ima if len(hash) != 40 || area.W <= 0 || size.Width <= 0 { // Not a standard thumb name with sha1 hash prefix. - if imageBuffer, err := os.ReadFile(fileName); err != nil { + if imageBuffer, err := os.ReadFile(fileName); err != nil { //nolint:gosec // file path comes from resolved thumbnails return nil, err } else { return imaging.Decode(bytes.NewReader(imageBuffer), imaging.AutoOrientation(true)) @@ -187,7 +188,7 @@ func openIdealThumbFile(fileName, hash string, area Area, size Size) (result ima fileName = name } - if imageBuffer, err := os.ReadFile(fileName); err != nil { + if imageBuffer, err := os.ReadFile(fileName); err != nil { //nolint:gosec // file path comes from resolved thumbnails return nil, err } else { return imaging.Decode(bytes.NewReader(imageBuffer)) diff --git a/internal/thumb/crop/request.go b/internal/thumb/crop/request.go index e30e83264..bf90380dd 100644 --- a/internal/thumb/crop/request.go +++ b/internal/thumb/crop/request.go @@ -31,7 +31,7 @@ func FromRequest(hash, area string, size Size, thumbPath string) (fileName strin cropBase := fmt.Sprintf("%s_%dx%d_crop_%s%s", hash, size.Width, size.Height, area, fs.ExtJpeg) cropName := filepath.Join(filepath.Dir(thumbName), cropBase) - imageBuffer, err := os.ReadFile(thumbName) + imageBuffer, err := os.ReadFile(thumbName) //nolint:gosec // thumbName resolved from validated cache path if err != nil { return "", err diff --git a/internal/thumb/crop/sizes.go b/internal/thumb/crop/sizes.go index a5876ad28..dc846b65f 100644 --- a/internal/thumb/crop/sizes.go +++ b/internal/thumb/crop/sizes.go @@ -3,9 +3,11 @@ package crop import "github.com/photoprism/photoprism/internal/thumb" var ( + // DefaultOptions are the resample options applied to most crop sizes. DefaultOptions = []thumb.ResampleOption{thumb.ResampleFillCenter, thumb.ResampleDefault} ) +// Size defines a thumbnail size and resample options. type Size struct { Name Name `json:"name"` Source Name `json:"-"` @@ -15,6 +17,7 @@ type Size struct { Options []thumb.ResampleOption `json:"-"` } +// SizeMap maps size names to size definitions. type SizeMap map[Name]Size // Sizes contains the properties of all thumbnail sizes. diff --git a/internal/thumb/frame/angle.go b/internal/thumb/frame/angle.go index a84dc056c..1ab656626 100644 --- a/internal/thumb/frame/angle.go +++ b/internal/thumb/frame/angle.go @@ -20,5 +20,5 @@ func RandomAngle(max float64) float64 { r := 2 * max - return (rand.Float64() - 0.5) * r + return (rand.Float64() - 0.5) * r //nolint:gosec // visual randomness only } diff --git a/internal/thumb/frame/frame.go b/internal/thumb/frame/frame.go index f6f859675..236f13156 100644 --- a/internal/thumb/frame/frame.go +++ b/internal/thumb/frame/frame.go @@ -24,6 +24,8 @@ Additional information can be found in our Developer Guide: */ package frame +// Type defines the frame style identifier. type Type string +// Polaroid represents the Polaroid-style frame type. const Polaroid = "polaroid" diff --git a/internal/thumb/frame/point.go b/internal/thumb/frame/point.go index 8a307343d..26d481963 100644 --- a/internal/thumb/frame/point.go +++ b/internal/thumb/frame/point.go @@ -16,14 +16,14 @@ func RandomPoint(xMin, yMin, xMax, yMax int) image.Point { } xDiff := float64(xMax - xMin) - x := xMin + int(rand.Float64()*xDiff) + x := xMin + int(rand.Float64()*xDiff) //nolint:gosec // visual randomness only if yMin > yMax { yMin = yMax } yDiff := float64(yMax - yMin) - y := yMin + int(rand.Float64()*yDiff) + y := yMin + int(rand.Float64()*yDiff) //nolint:gosec // visual randomness only return image.Pt(x, y) } From 7fbc0897c394db3f799b7d423604d5701bb06fb6 Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sun, 23 Nov 2025 08:51:31 +0100 Subject: [PATCH 042/195] CI: Apply Go linter recommendations to entity.ManuallyAddedFaces() #5330 Signed-off-by: Michael Mayer --- internal/entity/query/faces.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/entity/query/faces.go b/internal/entity/query/faces.go index 668ec8a1f..017945d5a 100644 --- a/internal/entity/query/faces.go +++ b/internal/entity/query/faces.go @@ -86,13 +86,13 @@ func Faces(knownOnly, unmatchedOnly, hidden, ignored bool) (result entity.Faces, } // ManuallyAddedFaces returns all manually added face clusters for the specified subj_uid, or all subjects if "". -func ManuallyAddedFaces(hidden, ignored bool, subj_uid string) (result entity.Faces, err error) { +func ManuallyAddedFaces(hidden, ignored bool, subjUid string) (result entity.Faces, err error) { stmt := Db(). Where("face_hidden = ?", hidden). Where("face_src = ?", entity.SrcManual) - if subj_uid != "" { - stmt = stmt.Where("subj_uid = ?", subj_uid) + if subjUid != "" { + stmt = stmt.Where("subj_uid = ?", subjUid) } else { stmt = stmt.Where("subj_uid <> ''") } @@ -213,7 +213,7 @@ func PurgeOrphanFaces(faceIds []string, ignored bool) (affected int, err error) } else { // see https://github.com/photoprism/photoprism/issues/3124#issuecomment-2558299360 log.Debugf("faces: no affected rows for purge in batch %d - %d", i, j) - //affected += len(ids) + // affected += len(ids) } } From 6fabd096eee611cd55860e39b8283c3f49193e5a Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sun, 23 Nov 2025 09:38:34 +0100 Subject: [PATCH 043/195] Batch Edit: Add mutex.BatchEdit to prevent concurrent edits #271 Signed-off-by: Michael Mayer --- internal/api/batch_photos_edit.go | 8 ++++- internal/mutex/activities.go | 11 +++++- internal/photoprism/batch/README.md | 4 ++- internal/photoprism/batch/save.go | 21 ++++++++++++ internal/photoprism/batch/save_test.go | 47 ++++++++++++++++++++++---- 5 files changed, 82 insertions(+), 9 deletions(-) diff --git a/internal/api/batch_photos_edit.go b/internal/api/batch_photos_edit.go index 7992c8229..687f8b2fd 100644 --- a/internal/api/batch_photos_edit.go +++ b/internal/api/batch_photos_edit.go @@ -1,6 +1,7 @@ package api import ( + "errors" "net/http" "github.com/dustin/go-humanize/english" @@ -92,7 +93,12 @@ func BatchPhotosEdit(router *gin.RouterGroup) { if frm.Values != nil { outcome, saveErr := batch.PrepareAndSavePhotos(photos, preloadedPhotos, frm.Values) - if saveErr != nil { + switch { + case errors.Is(saveErr, batch.ErrBatchEditBusy), errors.Is(saveErr, batch.ErrBatchEditCanceled): + log.Warnf("batch: %s", saveErr) + AbortBusy(c) + return + case saveErr != nil: log.Errorf("batch: failed to persist photo updates: %s", saveErr) AbortUnexpectedError(c) return diff --git a/internal/mutex/activities.go b/internal/mutex/activities.go index c5496953f..c5988ea09 100644 --- a/internal/mutex/activities.go +++ b/internal/mutex/activities.go @@ -10,6 +10,7 @@ var ( VisionWorker = Activity{} FacesWorker = Activity{} UpdatePeople = Activity{} + BatchEdit = Activity{} ) // CancelAll requests to stop all activities. @@ -22,9 +23,17 @@ func CancelAll() { VisionWorker.Cancel() FacesWorker.Cancel() UpdatePeople.Cancel() + BatchEdit.Cancel() } // WorkersRunning checks if a worker is currently running. func WorkersRunning() bool { - return IndexWorker.Running() || SyncWorker.Running() || BackupWorker.Running() || ShareWorker.Running() || MetaWorker.Running() || VisionWorker.Running() || FacesWorker.Running() + return IndexWorker.Running() || + SyncWorker.Running() || + BackupWorker.Running() || + ShareWorker.Running() || + MetaWorker.Running() || + VisionWorker.Running() || + FacesWorker.Running() || + BatchEdit.Running() } diff --git a/internal/photoprism/batch/README.md b/internal/photoprism/batch/README.md index 73db2044c..6e4ff5620 100644 --- a/internal/photoprism/batch/README.md +++ b/internal/photoprism/batch/README.md @@ -1,6 +1,6 @@ ## PhotoPrism — Batch Edit Package -**Last Updated:** November 21, 2025 +**Last Updated:** November 23, 2025 ### Overview @@ -13,6 +13,7 @@ The `internal/photoprism/batch` package implements the form schema (`PhotosForm` - Community requests such as [Issue #271](https://github.com/photoprism/photoprism/issues/271) emphasized the need to bulk-edit core metadata (location, time zone, titles) instead of repeating the same change photo by photo. - [PR #5324](https://github.com/photoprism/photoprism/pull/5324) introduced the modern batch dialog, chip controls, and validation rules that this package still serves. - Batch edits run inside regular API workers; there is no dedicated job queue. We therefore optimize for O(n) database work across selected photos, avoid global locks, and offload heavy recomputation to existing workers (meta, labels, search index). +- Batch edit requests run under a dedicated `mutex.BatchEdit` activity to serialize concurrent edits and cancel the background meta worker while changes are applied. - Frontend components expect round-trip metadata even for fields that are not yet editable (ISO, focal length, copyright, etc.), so the form structs intentionally contain more data than the dialog renders. #### Goals @@ -21,6 +22,7 @@ The `internal/photoprism/batch` package implements the form schema (`PhotosForm` - Guarantee that album/label updates obey ACLs and deduplicate creations, even when multiple requests fire concurrently. - Minimize writes by only persisting changed columns and deferring derived work (labels, keyword re-indexing) to background workers. - Return refreshed photo models so the UI can immediately render the persisted state without extra queries. +- Serialize batch edits with `mutex.BatchEdit` so only one batch edit runs at a time and shutdown can cancel ongoing requests; the meta worker is canceled while edits are applied. #### Non-Goals diff --git a/internal/photoprism/batch/save.go b/internal/photoprism/batch/save.go index 5d938c4f0..4fa248589 100644 --- a/internal/photoprism/batch/save.go +++ b/internal/photoprism/batch/save.go @@ -1,6 +1,7 @@ package batch import ( + "errors" "fmt" "time" @@ -8,6 +9,7 @@ import ( "github.com/photoprism/photoprism/internal/entity/query" "github.com/photoprism/photoprism/internal/entity/search" "github.com/photoprism/photoprism/internal/form" + "github.com/photoprism/photoprism/internal/mutex" "github.com/photoprism/photoprism/pkg/txt" ) @@ -38,6 +40,12 @@ type MutationStats struct { LabelErrors int } +// ErrBatchEditBusy is returned when another batch edit is already running. +var ErrBatchEditBusy = errors.New("edit already running") + +// ErrBatchEditCanceled is returned when a batch edit was canceled (e.g. during shutdown). +var ErrBatchEditCanceled = errors.New("edit canceled") + // NewPhotoSaveRequest converts the batch values into a form.Photo and bundles it with the // target entity so callers outside this package do not have to depend on ConvertToPhotoForm. func NewPhotoSaveRequest(photo *entity.Photo, values *PhotosForm) (*PhotoSaveRequest, error) { @@ -141,6 +149,15 @@ func PrepareAndSavePhotos(photos search.PhotoResults, preloaded map[string]*enti return result, nil } + if err := mutex.BatchEdit.Start(); err != nil { + return nil, ErrBatchEditBusy + } + + defer mutex.BatchEdit.Stop() + + // Prevent concurrent metadata worker runs while batch updates are being applied. + mutex.MetaWorker.Cancel() + requests, preloaded, mutationStats := PreparePhotoSaveRequests(photos, result.Preloaded, values) result.Requests = requests @@ -212,6 +229,10 @@ func SavePhotos(requests []*PhotoSaveRequest) ([]bool, error) { anySaved := false for i, req := range requests { + if mutex.BatchEdit.Canceled() { + return results, ErrBatchEditCanceled + } + saved, err := savePhoto(req) if err != nil { diff --git a/internal/photoprism/batch/save_test.go b/internal/photoprism/batch/save_test.go index 720183a05..f9da3fba5 100644 --- a/internal/photoprism/batch/save_test.go +++ b/internal/photoprism/batch/save_test.go @@ -9,6 +9,7 @@ import ( "github.com/photoprism/photoprism/internal/entity" "github.com/photoprism/photoprism/internal/entity/search" + "github.com/photoprism/photoprism/internal/mutex" ) // TestSavePhotos covers SavePhotos scenarios. @@ -53,7 +54,6 @@ func TestSavePhotos(t *testing.T) { "edited_at": originalEdited, }) }) - t.Run("UpdatesDateFields", func(t *testing.T) { fixture := entity.PhotoFixtures.Get("Photo02") photo := entity.FindPhoto(entity.Photo{PhotoUID: fixture.PhotoUID}) @@ -103,7 +103,6 @@ func TestSavePhotos(t *testing.T) { "edited_at": originalEdited, }) }) - t.Run("RemovesStrings", func(t *testing.T) { fixture := entity.PhotoFixtures.Get("Photo03") photo := entity.FindPhoto(entity.Photo{PhotoUID: fixture.PhotoUID}) @@ -182,7 +181,6 @@ func TestNewPhotoSaveRequest(t *testing.T) { assert.Nil(t, req) assert.Error(t, err) }) - t.Run("BuildsRequest", func(t *testing.T) { fixture := entity.PhotoFixtures.Get("Photo02") photo := entity.FindPhoto(entity.Photo{PhotoUID: fixture.PhotoUID}) @@ -211,7 +209,6 @@ func TestPreparePhotoSaveRequests(t *testing.T) { assert.Equal(t, preloaded, updated) assert.Equal(t, MutationStats{}, stats) }) - t.Run("LoadsMissingPhoto", func(t *testing.T) { fixture := entity.PhotoFixtures.Get("Photo01") values := &PhotosForm{PhotoTitle: String{Value: "Prepared", Action: ActionUpdate}} @@ -224,7 +221,6 @@ func TestPreparePhotoSaveRequests(t *testing.T) { assert.Contains(t, updated, fixture.PhotoUID) assert.Equal(t, MutationStats{}, stats) }) - t.Run("SkipsMissing", func(t *testing.T) { values := &PhotosForm{PhotoTitle: String{Value: "Prepared", Action: ActionUpdate}} photos := search.PhotoResults{{PhotoUID: "pt_does_not_exist"}} @@ -246,7 +242,6 @@ func TestPrepareAndSavePhotos(t *testing.T) { assert.Len(t, result.Requests, 0) assert.Len(t, result.Results, 0) }) - t.Run("PersistsChanges", func(t *testing.T) { fixture := entity.PhotoFixtures.Get("Photo02") photo := entity.FindPhoto(entity.Photo{PhotoUID: fixture.PhotoUID}) @@ -277,6 +272,46 @@ func TestPrepareAndSavePhotos(t *testing.T) { "photo_favorite": originalFavorite, }) }) + t.Run("RejectsConcurrentBatchEdit", func(t *testing.T) { + require.NoError(t, mutex.BatchEdit.Start()) + defer mutex.BatchEdit.Stop() + + result, err := PrepareAndSavePhotos(search.PhotoResults{}, nil, &PhotosForm{}) + assert.Nil(t, result) + assert.ErrorIs(t, err, ErrBatchEditBusy) + }) + t.Run("CancelsMetaWorkerWhenRunning", func(t *testing.T) { + require.NoError(t, mutex.MetaWorker.Start()) + t.Cleanup(func() { mutex.MetaWorker.Stop() }) + + result, err := PrepareAndSavePhotos(search.PhotoResults{}, nil, &PhotosForm{}) + require.NoError(t, err) + require.NotNil(t, result) + + assert.True(t, mutex.MetaWorker.Canceled()) + }) + t.Run("AbortsOnCancellation", func(t *testing.T) { + fixture := entity.PhotoFixtures.Get("Photo02") + photo := entity.FindPhoto(entity.Photo{PhotoUID: fixture.PhotoUID}) + require.NotNil(t, photo) + + values := &PhotosForm{ + PhotoFavorite: Bool{Value: !photo.PhotoFavorite, Action: ActionUpdate}, + } + + req, err := NewPhotoSaveRequest(photo, values) + require.NoError(t, err) + + require.NoError(t, mutex.BatchEdit.Start()) + t.Cleanup(func() { mutex.BatchEdit.Stop() }) + + mutex.BatchEdit.Cancel() + + results, err := SavePhotos([]*PhotoSaveRequest{req}) + assert.ErrorIs(t, err, ErrBatchEditCanceled) + assert.Len(t, results, 1) + assert.False(t, results[0]) + }) } // restorePhoto rewinds DB state for the provided fixture so tests stay isolated. From 19f083c7199b6237ec67aa65204edf1f9f73ca3c Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sun, 23 Nov 2025 12:56:58 +0100 Subject: [PATCH 044/195] Thumbs: Enhance embedding of ICC profiles based on InteropIndex #5178 Signed-off-by: Michael Mayer --- AGENTS.md | 3 +- assets/profiles/icc/NOTICE | 156 ++++++++++---- .../icc/{adobe_rgb_compat.icc => a98.icc} | Bin assets/profiles/icc/adobecompat-v2.icc | Bin 0 -> 374 bytes assets/profiles/icc/adobecompat-v4.icc | Bin 0 -> 464 bytes assets/profiles/icc/applecompat-v2.icc | Bin 0 -> 374 bytes assets/profiles/icc/applecompat-v4.icc | Bin 0 -> 464 bytes .../profiles/icc/cgats001compat-v2-micro.icc | Bin 0 -> 8464 bytes assets/profiles/icc/colormatchcompat-v2.icc | Bin 0 -> 374 bytes assets/profiles/icc/colormatchcompat-v4.icc | Bin 0 -> 464 bytes assets/profiles/icc/dci-p3-v4.icc | Bin 0 -> 464 bytes assets/profiles/icc/displayp3-v2-magic.icc | Bin 0 -> 736 bytes assets/profiles/icc/displayp3-v2-micro.icc | Bin 0 -> 456 bytes assets/profiles/icc/displayp3-v4.icc | Bin 0 -> 480 bytes .../profiles/icc/displayp3compat-v2-magic.icc | Bin 0 -> 736 bytes .../profiles/icc/displayp3compat-v2-micro.icc | Bin 0 -> 456 bytes assets/profiles/icc/displayp3compat-v4.icc | Bin 0 -> 480 bytes assets/profiles/icc/prophoto-v2-magic.icc | Bin 0 -> 756 bytes assets/profiles/icc/prophoto-v2-micro.icc | Bin 0 -> 496 bytes assets/profiles/icc/prophoto-v4.icc | Bin 0 -> 480 bytes assets/profiles/icc/rec2020-g24-v4.icc | Bin 0 -> 464 bytes assets/profiles/icc/rec2020-v2-magic.icc | Bin 0 -> 790 bytes assets/profiles/icc/rec2020-v2-micro.icc | Bin 0 -> 460 bytes assets/profiles/icc/rec2020-v4.icc | Bin 0 -> 480 bytes .../profiles/icc/rec2020compat-v2-magic.icc | Bin 0 -> 790 bytes .../profiles/icc/rec2020compat-v2-micro.icc | Bin 0 -> 460 bytes assets/profiles/icc/rec2020compat-v4.icc | Bin 0 -> 480 bytes assets/profiles/icc/rec601ntsc-v2-magic.icc | Bin 0 -> 738 bytes assets/profiles/icc/rec601ntsc-v2-micro.icc | Bin 0 -> 460 bytes assets/profiles/icc/rec601ntsc-v4.icc | Bin 0 -> 480 bytes assets/profiles/icc/rec601pal-v2-magic.icc | Bin 0 -> 738 bytes assets/profiles/icc/rec601pal-v2-micro.icc | Bin 0 -> 460 bytes assets/profiles/icc/rec601pal-v4.icc | Bin 0 -> 480 bytes assets/profiles/icc/rec709-v2-magic.icc | Bin 0 -> 738 bytes assets/profiles/icc/rec709-v2-micro.icc | Bin 0 -> 460 bytes assets/profiles/icc/rec709-v4.icc | Bin 0 -> 480 bytes assets/profiles/icc/scrgb-v2.icc | Bin 0 -> 372 bytes assets/profiles/icc/sgrey-v2-magic.icc | Bin 0 -> 616 bytes assets/profiles/icc/sgrey-v2-micro.icc | Bin 0 -> 336 bytes assets/profiles/icc/sgrey-v2-nano.icc | Bin 0 -> 290 bytes assets/profiles/icc/sgrey-v4.icc | Bin 0 -> 360 bytes assets/profiles/icc/srgb-v2-magic.icc | Bin 0 -> 736 bytes assets/profiles/icc/srgb-v2-micro.icc | Bin 0 -> 456 bytes assets/profiles/icc/srgb-v2-nano.icc | Bin 0 -> 410 bytes assets/profiles/icc/srgb-v4.icc | Bin 0 -> 480 bytes assets/profiles/icc/widegamutcompat-v2.icc | Bin 0 -> 374 bytes assets/profiles/icc/widegamutcompat-v4.icc | Bin 0 -> 464 bytes internal/thumb/README.md | 65 ++++++ internal/thumb/icc.go | 194 ++++++++++++++++-- internal/thumb/icc_test.go | 55 +++++ internal/thumb/testdata/interop_index_r98.jpg | Bin 0 -> 540666 bytes .../thumb/testdata/interop_index_srgb_icc.jpg | Bin 0 -> 65870 bytes internal/thumb/testdata/interop_index_thm.jpg | Bin 0 -> 540666 bytes internal/thumb/vips.go | 46 +---- internal/thumb/vips_icc.go | 78 +++++++ internal/thumb/vips_icc_test.go | 75 +++++++ 56 files changed, 570 insertions(+), 102 deletions(-) rename assets/profiles/icc/{adobe_rgb_compat.icc => a98.icc} (100%) create mode 100644 assets/profiles/icc/adobecompat-v2.icc create mode 100644 assets/profiles/icc/adobecompat-v4.icc create mode 100644 assets/profiles/icc/applecompat-v2.icc create mode 100644 assets/profiles/icc/applecompat-v4.icc create mode 100644 assets/profiles/icc/cgats001compat-v2-micro.icc create mode 100644 assets/profiles/icc/colormatchcompat-v2.icc create mode 100644 assets/profiles/icc/colormatchcompat-v4.icc create mode 100644 assets/profiles/icc/dci-p3-v4.icc create mode 100644 assets/profiles/icc/displayp3-v2-magic.icc create mode 100644 assets/profiles/icc/displayp3-v2-micro.icc create mode 100644 assets/profiles/icc/displayp3-v4.icc create mode 100644 assets/profiles/icc/displayp3compat-v2-magic.icc create mode 100644 assets/profiles/icc/displayp3compat-v2-micro.icc create mode 100644 assets/profiles/icc/displayp3compat-v4.icc create mode 100644 assets/profiles/icc/prophoto-v2-magic.icc create mode 100644 assets/profiles/icc/prophoto-v2-micro.icc create mode 100644 assets/profiles/icc/prophoto-v4.icc create mode 100644 assets/profiles/icc/rec2020-g24-v4.icc create mode 100644 assets/profiles/icc/rec2020-v2-magic.icc create mode 100644 assets/profiles/icc/rec2020-v2-micro.icc create mode 100644 assets/profiles/icc/rec2020-v4.icc create mode 100644 assets/profiles/icc/rec2020compat-v2-magic.icc create mode 100644 assets/profiles/icc/rec2020compat-v2-micro.icc create mode 100644 assets/profiles/icc/rec2020compat-v4.icc create mode 100644 assets/profiles/icc/rec601ntsc-v2-magic.icc create mode 100644 assets/profiles/icc/rec601ntsc-v2-micro.icc create mode 100644 assets/profiles/icc/rec601ntsc-v4.icc create mode 100644 assets/profiles/icc/rec601pal-v2-magic.icc create mode 100644 assets/profiles/icc/rec601pal-v2-micro.icc create mode 100644 assets/profiles/icc/rec601pal-v4.icc create mode 100644 assets/profiles/icc/rec709-v2-magic.icc create mode 100644 assets/profiles/icc/rec709-v2-micro.icc create mode 100644 assets/profiles/icc/rec709-v4.icc create mode 100644 assets/profiles/icc/scrgb-v2.icc create mode 100644 assets/profiles/icc/sgrey-v2-magic.icc create mode 100644 assets/profiles/icc/sgrey-v2-micro.icc create mode 100644 assets/profiles/icc/sgrey-v2-nano.icc create mode 100644 assets/profiles/icc/sgrey-v4.icc create mode 100644 assets/profiles/icc/srgb-v2-magic.icc create mode 100644 assets/profiles/icc/srgb-v2-micro.icc create mode 100644 assets/profiles/icc/srgb-v2-nano.icc create mode 100644 assets/profiles/icc/srgb-v4.icc create mode 100644 assets/profiles/icc/widegamutcompat-v2.icc create mode 100644 assets/profiles/icc/widegamutcompat-v4.icc create mode 100644 internal/thumb/README.md create mode 100644 internal/thumb/icc_test.go create mode 100644 internal/thumb/testdata/interop_index_r98.jpg create mode 100644 internal/thumb/testdata/interop_index_srgb_icc.jpg create mode 100644 internal/thumb/testdata/interop_index_thm.jpg create mode 100644 internal/thumb/vips_icc.go create mode 100644 internal/thumb/vips_icc_test.go diff --git a/AGENTS.md b/AGENTS.md index a400e89be..7bb17f456 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,6 @@ # PhotoPrism® Repository Guidelines -**Last Updated:** November 21, 2025 +**Last Updated:** November 23, 2025 ## Purpose @@ -212,6 +212,7 @@ Note: Across our public documentation, official images, and in production, the c ## Code Style & Lint - Go: run `make fmt-go swag-fmt` to reformat the backend code + Swagger annotations (see `Makefile` for additional targets) + - Run `make lint-go` (golangci-lint) after Go changes; prefer `golangci-lint run ./internal//...` for focused edits. - Doc comments for packages and exported identifiers must be complete sentences that begin with the name of the thing being described and end with a period. - All newly added functions, including unexported helpers, must have a concise doc comment that explains their behavior. - For short examples inside comments, indent code rather than using backticks; godoc treats indented blocks as preformatted. diff --git a/assets/profiles/icc/NOTICE b/assets/profiles/icc/NOTICE index 661d66272..d95f025fd 100644 --- a/assets/profiles/icc/NOTICE +++ b/assets/profiles/icc/NOTICE @@ -1,54 +1,122 @@ +================================================================================ +================================================================================ -Files: compatibleWithAdobeRGB1998.icc -Source: Debian icc-profiles-free - https://salsa.debian.org/debian/icc-profiles-free/-/tree/a7a3c11b8a6d3bc2937447183b87dc89de9d2388/icc-profiles-openicc/default_profiles/base -Copyright: Kai-Uwe Behrmann - Marti Maria - Photogamut - Graeme Gill - ColorSolutions -License: Zlib + Third-Party ICC Profiles for PhotoPrism + +-------------------------------------------------------------------------------- + +The following 3rd-party ICC profiles may be used by or distributed with +PhotoPrism. Any information relevant to third-party vendors listed below are +collected using common, reasonable means. + +Date generated: 2025-11-23 + +-------------------------------------------------------------------------------- + +Files: a98.icc (compatibleWithAdobeRGB1998.icc) +Source: OpenICC "compatibleWithAdobeRGB1998.icc" via Debian icc-profiles-free +URL: https://salsa.debian.org/debian/icc-profiles-free/-/blob/a7a3c11b8a6d3bc2937447183b87dc89de9d2388/icc-profiles-openicc/default_profiles/base/compatibleWithAdobeRGB1998.icc +License: zlib/libpng +Checksum (md5): 826a1e13374e3dc34f9872f31ec028c8 + +The zlib/libpng License + +Copyright (c) Graeme Gill + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: -License: Zlib - The zlib/libpng License - . - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - . - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - . 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. - . + 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. - . + 3. This notice may not be removed or altered from any source distribution. - . - NO WARRANTY - . - BECAUSE THE DATA IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY - FOR THE DATA, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN - OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES - PROVIDE THE DATA "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED - OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS - TO THE QUALITY AND PERFORMANCE OF THE DATA IS WITH YOU. SHOULD THE - DATA PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, - REPAIR OR CORRECTION. - . - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING - WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR - REDISTRIBUTE THE DATA AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, - INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING - OUT OF THE USE OR INABILITY TO USE THE DATA (INCLUDING BUT NOT LIMITED - TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY - YOU OR THIRD PARTIES OR A FAILURE OF THE DATA TO OPERATE WITH ANY OTHER - PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE - POSSIBILITY OF SUCH DAMAGES. \ No newline at end of file + +The provided ICC Profiles in the package are called DATA in the following +statement: + + NO WARRANTY + + BECAUSE THE DATA IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE DATA, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE DATA "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE DATA IS WITH YOU. SHOULD THE +DATA PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE DATA AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE DATA (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE DATA TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + +-------------------------------------------------------------------------------- + +Files: adobecompat-v2.icc, adobecompat-v4.icc, applecompat-v2.icc, applecompat-v4.icc, cgats001compat-v2-micro.icc, colormatchcompat-v2.icc, colormatchcompat-v4.icc, dci-p3-v4.icc, displayp3-v2-magic.icc, displayp3-v2-micro.icc, displayp3-v4.icc, displayp3compat-v2-magic.icc, displayp3compat-v2-micro.icc, displayp3compat-v4.icc, prophoto-v2-magic.icc, prophoto-v2-micro.icc, prophoto-v4.icc, rec2020-g24-v4.icc, rec2020-v2-magic.icc, rec2020-v2-micro.icc, rec2020-v4.icc, rec2020compat-v2-magic.icc, rec2020compat-v2-micro.icc, rec2020compat-v4.icc, rec601ntsc-v2-magic.icc, rec601ntsc-v2-micro.icc, rec601ntsc-v4.icc, rec601pal-v2-magic.icc, rec601pal-v2-micro.icc, rec601pal-v4.icc, rec709-v2-magic.icc, rec709-v2-micro.icc, rec709-v4.icc, scrgb-v2.icc, sgrey-v2-magic.icc, sgrey-v2-micro.icc, sgrey-v2-nano.icc, sgrey-v4.icc, srgb-v2-magic.icc, srgb-v2-micro.icc, srgb-v2-nano.icc, srgb-v4.icc, widegamutcompat-v2.icc, widegamutcompat-v4.icc +Source: Compact-ICC-Profiles via Debian icc-profiles-free +URL: https://salsa.debian.org/debian/icc-profiles-free/-/tree/a7a3c11b8a6d3bc2937447183b87dc89de9d2388/Compact-ICC-Profiles/profiles +License: CC0-1.0 (Public Domain Dedication) +Checksums (md5): + adobecompat-v2.icc 08220aa4b4e4259ec3c446a35197d89b + adobecompat-v4.icc fbf912760a8d14e496ff389c29c3132d + applecompat-v2.icc 21453c734d9364abceacc7ab837019ec + applecompat-v4.icc fc399558e27a0d53748820cff2a98a2b + cgats001compat-v2-micro.icc 56a85233ee08fa7527875be36cf426d6 + colormatchcompat-v2.icc 9f2a755b4b3069f46f4eaef11e24926d + colormatchcompat-v4.icc 9e119efb0abaa31e955f8a30eeabc58c + dci-p3-v4.icc bdedf9e7ad0b93ed8f85f8c3ebdc4223 + displayp3-v2-magic.icc 6748fcfd56d38770a02c023bbd6d0529 + displayp3-v2-micro.icc 2615293123ddc4366af3da39455c3d7a + displayp3-v4.icc 32dc35d6a113b86cbc31bd1281e3baed + displayp3compat-v2-magic.icc 05fb82a702e27438ecec47a2f120cdcd + displayp3compat-v2-micro.icc 2ef6c295ac5d05760c1a4cf1668951a3 + displayp3compat-v4.icc c34c90451326b183916f05b3ae41d920 + prophoto-v2-magic.icc 15a31d407cf35662fbe4513f6204bfdf + prophoto-v2-micro.icc 445c1a3f3f1a20aab68e76838d3ed334 + prophoto-v4.icc 8f4523255234753cd2d3111d8f09b184 + rec2020-g24-v4.icc 3c7afbfff612d10a10775c167d36b51e + rec2020-v2-magic.icc 3b5846fb69faa53ebdfd29061f17b0e3 + rec2020-v2-micro.icc 13d1e96875c35f7d5ebaf0f6a3d16357 + rec2020-v4.icc 297c644758a979abe62349ca1ff416d5 + rec2020compat-v2-magic.icc 9aa85534e81c275bc0627badf157580a + rec2020compat-v2-micro.icc fa39fc95daece7dcaff18e7265082368 + rec2020compat-v4.icc 66839229639bb55bc443b490fe302364 + rec601ntsc-v2-magic.icc 53ee12707ac87a8e3b3452af4a325e29 + rec601ntsc-v2-micro.icc af05afec2146917a67445ac6cb5ca61d + rec601ntsc-v4.icc cc57dd6fa3d6f08e43e0f70b5376c00a + rec601pal-v2-magic.icc f706fd528cedcd1c88dac50073112f94 + rec601pal-v2-micro.icc d193e01949eb5347b0d16d2b3ccdabcf + rec601pal-v4.icc dd7cd61d6ee14a521c9fb5afa2803e8e + rec709-v2-magic.icc 47f09046656a2f0d66117a9c1b15e137 + rec709-v2-micro.icc f91edc9f3ff1390c842bd9e8759688b0 + rec709-v4.icc 0339e2a70940aefd9237311889d065e6 + scrgb-v2.icc a841263101bdf48fb9b81486f5451f2d + sgrey-v2-magic.icc ef6221686b517e4665480639202dacd5 + sgrey-v2-micro.icc ca08451dba57ca1e910330cda37515ad + sgrey-v2-nano.icc 57d72e3f6437d65c618ebcdb1f6fa1bd + sgrey-v4.icc b93b1e31e75243ea08fbdab9e82f7cbe + srgb-v2-magic.icc 5967f401f9a54913a283942710cef93c + srgb-v2-micro.icc e8de3a5b44d70610306b0a20225701d1 + srgb-v2-nano.icc e060a57b7a057f7f0bdb859b68db60e9 + srgb-v4.icc 3c6a277ddee033ad090ba22b8323dcaf + widegamutcompat-v2.icc 63c0165987ee94c8c7f2c68749e0418d + widegamutcompat-v4.icc 4ccb168ae2f7daa51d3cef7df6fa6f16 + +-------------------------------------------------------------------------------- diff --git a/assets/profiles/icc/adobe_rgb_compat.icc b/assets/profiles/icc/a98.icc similarity index 100% rename from assets/profiles/icc/adobe_rgb_compat.icc rename to assets/profiles/icc/a98.icc diff --git a/assets/profiles/icc/adobecompat-v2.icc b/assets/profiles/icc/adobecompat-v2.icc new file mode 100644 index 0000000000000000000000000000000000000000..47fb1b1e89b20e2accc57353c300ff2155215a95 GIT binary patch literal 374 zcmZQzU@UWVadKr6U|`72D=7+ccT$Lmj8b5K#K6oT!obPE#~_=STwLHA>=wcR1jUKv z#mOZ_IUqIye7nZL2;yDV%}C5ki8~~D`*z5g6Ze?*9eMq5D-S{)Ru*SUYH>19<^vGN zCl?fzFfcHJ^zf9I6oA+gK(Nf0*3Z6ZY>LC!!m89+8)Iubhx zi4C<2sDy#l(b58_9sx>HD}eSfFgQCKFo41UBJ(*A>^DZSGYGLcKsWg>Wnf_WgAg<6 iWnfqUbRYjzgqWHk1A~A*1H-;K$)!bQ5c3!qnT!GUPeD}x literal 0 HcmV?d00001 diff --git a/assets/profiles/icc/adobecompat-v4.icc b/assets/profiles/icc/adobecompat-v4.icc new file mode 100644 index 0000000000000000000000000000000000000000..14accc116da9eb41b57d5c5422c0b6c93ffb6c3a GIT binary patch literal 464 zcmZQzV7%by;^fMrz`&53S5g$@?xYYA8KuDfl!1jont=<584{C=3w(p!LKuLcII+Aq zxuhru#72N`*BBT7iFqlDWtn9gWqKaiwR@FHXm&&))L~_DrKA=o17-dIu}X45 zQ3(SBqXLkvR9;d5V!Hs@BFPzvDL{4vkgZb$bRdx30uoP$uxEhSNf7oL5W6TO$Qh{S z0FW(^j>JwvV&~?Rg6stWkn4F;^Fo7xGzSpNFgP+;GFUJ;<50r}R^!az%wWKv2nsJC z9|#cPP@HCL1QufCabjR#z4!nBXH^CU_Q?zke<%F^|9j#8|9=-TFfiX?U^tM6&_73l ufx&+%0|Uz+gqTS$1H%Fx1_u7A2r)H7pttlH81~I6NGwVO+XEt)jEw=wcR1jUKv z#mOZ_IUqIye7nZL2;yDV%}C5kSwE#Ye)gT&d_En2vR8jTVT@3RmBpEoTAU1&`2fW6 z$pu9v3=E7sKsHZ#Ndbs00c4950UZTo>wwtl5Viw|odjWn+$K^K666e2lL2J&r6aME zkl0YWfJzux9RqxT>JgwMwE}1#1B0`(0Rt!uATpl=!G2>DJA)9b+{?h=7{tK9vI`*= k_ML%YjXnc|EHgq(O^Ja)sGfmgV?lChQ5nQM21drS08uVMYybcN literal 0 HcmV?d00001 diff --git a/assets/profiles/icc/applecompat-v4.icc b/assets/profiles/icc/applecompat-v4.icc new file mode 100644 index 0000000000000000000000000000000000000000..144c90b8d99040d86a05144e5b7a543705e8850e GIT binary patch literal 464 zcmZQzV7%by;^fMrz`&53S5g$@?xYYA8KuDfl!1jont=<584{C=3w(p!LKuLcII+Aq zxuhru#72N`*BBT7iFqkKa$FgO*5fTwn%bDVhWHQ0c7hG0UZcrw}8acA?z6-b`pfW2E;B3333Li zIRIn}q$9DDkl497r67Aj0OWd})V$DOAk6{9G7OFk0SrD2&N$Stfz>!OI5QY9D1yQZ z$Oi&MI25ND8-axwd7Ky+SnvJ+|5=rRfqgOq!`})2|Nmb2|Nq}b3=GV77#I%ZA@o=7 wWngd&Vqjp|g%At-&cLunpMgP^86l>o#K0g_&%m&;pdhg*5o`~LU_5gc0Bu5B%K!iX literal 0 HcmV?d00001 diff --git a/assets/profiles/icc/cgats001compat-v2-micro.icc b/assets/profiles/icc/cgats001compat-v2-micro.icc new file mode 100644 index 0000000000000000000000000000000000000000..b5a73495bfc7509837b300800d8197d94e83fab7 GIT binary patch literal 8464 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 U@I8Hy^1FWP|Gv&X!`9FL1Def^p8x;= literal 0 HcmV?d00001 diff --git a/assets/profiles/icc/colormatchcompat-v2.icc b/assets/profiles/icc/colormatchcompat-v2.icc new file mode 100644 index 0000000000000000000000000000000000000000..61d6211841e78fddc4188b329ed317e94ef43635 GIT binary patch literal 374 zcmZQzU@UWVadKr6U|`72D=7+ccT$Lmj8b5K#K6oT!obPE#~_=STwLHA>=wcR1jUKv z#mOZ_IUqIye7nZL2;yDV%}C5kSt0h(<8#dQB}2Vkbe^Ah(GWg#7#P^h5Mn{f j3=AtO85pFmBgB+E7#IXq85lMyB$pPILCj-dWIPK1$eBQt literal 0 HcmV?d00001 diff --git a/assets/profiles/icc/colormatchcompat-v4.icc b/assets/profiles/icc/colormatchcompat-v4.icc new file mode 100644 index 0000000000000000000000000000000000000000..5ead9176caed58b93c023749078c51c3c0c7fb2b GIT binary patch literal 464 zcmZQzV7%by;^fMrz`&53S5g$@?xYYA8KuDfl!1jont=<584{C=3w(p!LKuLcII+Aq zxuhru#72N`*BBT7iFqj&H7_L76=%KJ_D}cOp{UzY2z6LlTq&u=$v~MuK&+Bn zP*lRez^DLZE0vcNfY>fTwn%bDVhWHQ0c7hG0UZcrw}8acA?z6-b`pfW2E;B3333Li zIRIn}q$9DDkl497r67Aj0OWd})V$DOAk6{9G7OFk&J4Z`&N$Stfz>zz)fg}+g2D^P z2LePm6sH**LB#(5{r~^}KPdhSw22wW1_Pix(BA*=5&E0585rD>7#P^h5Mn{f3=AtO f85pFmBgB+E7#IXq85lMy6eJcULhJ#VcIGSqNB&~7 literal 0 HcmV?d00001 diff --git a/assets/profiles/icc/dci-p3-v4.icc b/assets/profiles/icc/dci-p3-v4.icc new file mode 100644 index 0000000000000000000000000000000000000000..f6c8b94319f944fd867256c0fe523f0af418bd97 GIT binary patch literal 464 zcmZQzV7!o%oLkJIz`&53S5g$@?xYYA8KuDfl!1jont=<584{C=3w(p!LKuLcII+Aq zxuhru#72N`*BBT7iFqlNn*)uGT#s(~xchWO@P=RC5bCh9xKdJ!lYugSfLJNH zps0j_fl&d(E-xtnv0Z>{k>rfT6d*eS$kr(WIuOWi0g0zW*fT)vBnW#Ah+PyCt2of=`VzgENBxgCZ!rfO>!c z5e~&^#ztTvMxkd644m)(|Nqv@z`$n(RCD70|3AV1|Nnaqv^JW7;m&=8{+dz-1}l#L q|Noyshy}f1VAy(tfq`2PA*Nml^p*kx!-;@`#G*v7Js^T<<}3if^I)j} literal 0 HcmV?d00001 diff --git a/assets/profiles/icc/displayp3-v2-magic.icc b/assets/profiles/icc/displayp3-v2-magic.icc new file mode 100644 index 0000000000000000000000000000000000000000..623c50386feece5951612e9dbd8f0e766b3153ca GIT binary patch literal 736 zcmZQzV0w^~oLkH!z`&53S5g$@?xYYA8KuDfh=G|wgn^TRk3lvuxwybL*e!$s2#OQS zi<3)=azJbZ_;!tf5yZQ!n~|88^3j0n+C|Y%6&Jl7GwSr$_aoF{WpSpY7AFH`J^*oC zazRlE0|O%ukj+zGQUGF00NEl%Kt}=DIv{pBgzW%gCqdXCw}}*m1UUoMWB_qRIubhx zi4C<2sGNbNIKUW0!$C=E1<*bQ24`mj22dD4Bt8d%{l+ME1|imbpMk-4|NsC0cO%5S z_A@YSG-qJo+KCX;uwY;i!cf4_!?1$k2*X2021X@D zH^yAXsf-61KQJjUg)((B9bslk`&~Y%y&6*cI8k*uQWj zb6n;0_5w;bqU)Ee-qgxS}0~F_E-Fr z#7xONDK}{)8D^R1vKQp`%CA#cqBu`!j`Dn!rK%g%4ya$%c&o*wt*sNNTcfv1|DK_k zQIPQzlbdEL=EWArtQ4$kZ0_5++HY`FbeiTO;M(slID`1knFbH-OZZ)HB1d=B_l{B!o7Q~&=101lzp6#xJL literal 0 HcmV?d00001 diff --git a/assets/profiles/icc/displayp3-v2-micro.icc b/assets/profiles/icc/displayp3-v2-micro.icc new file mode 100644 index 0000000000000000000000000000000000000000..d7433bd1babf2de861f2b744c89a74e7daa8882f GIT binary patch literal 456 zcmZQzU_6nNoLkH!z`&53S5g$@?xYYA8KuDfh=G|wgn^TRk3lvuxwybL*e!$s2#OQS zi<3)=azJbZ_;!tf5yZQ!n~|88vSrm>dEpbxHeUlpTs5j@JVL0$%Hm8(Elvi?d;sFO z^DZSGYGNf`wR@W`~Uy{zZ)Uu zwV#1uqd5Zu*G`0(h6MwIAQuC}iJi%%MP(547_@+N4Z{z{IZUO@%`7L`{5XWTBzThe zz6hQd`6iJiqo?4kvPLsYug>_j0I^+wY?0)Q#1tSq0?5`W0y+@LZUKp>L)bGw>?8<#4TxP7666e2 za{$OzNJnBPA+d9FNeVS-PMGlMgO0fQnaynuRu z01*zwX~sriAx0i21_su9|NnnhWnf^R%)sz>!vFuj7ykeMcM$^v^Bo3;19=Gj&G#7? zZ1?~F|9>|^%xgab!$xxk2Ckh5F%1g_20<z;GSQ} literal 0 HcmV?d00001 diff --git a/assets/profiles/icc/displayp3compat-v2-magic.icc b/assets/profiles/icc/displayp3compat-v2-magic.icc new file mode 100644 index 0000000000000000000000000000000000000000..cfe315d39333cd92c907dafdeeb1350b780ac182 GIT binary patch literal 736 zcmZQzV0w^~oLkH!z`&53S5g$@?xYYA8KuDfh=G|wgn^TRk3lvuxwybL*e!$s2#OQS zi<3)=azJbZ_;!tf5yZQ!n~|88Qk~C#`hlyXnZ3V@>1nApNrXDAEY6hF;$)!A2Oy41 zE+{HtU|{3{vU$o&3P5ZLAX}se=qMmt2gFW?upL0`BnTViHj$!`AZMVO3?QyZM`9-- zv7vSWl{2sw2N(m@BS1-N1<*bQ24`mj22dD4WIhLi{l+ME1|imbpMk-4KS&-S<^|+$ zG-qJo+KLd|Z#NIj(Yg zavtWg=i1Nh#C?({gy$h|2_GllB7O({2Li2vx`MZax`ge7zlm%TEfg~o`zwA*Vy0xC zl$*4Y471F0*$Z-e<<}`JQJkkVM|r-=Qq_%W2h^`>yw&2;*47Est4ssH~0)XLIE literal 0 HcmV?d00001 diff --git a/assets/profiles/icc/displayp3compat-v2-micro.icc b/assets/profiles/icc/displayp3compat-v2-micro.icc new file mode 100644 index 0000000000000000000000000000000000000000..f201c4c2cf633ee4fb7a40f0e69a43ca0649c40b GIT binary patch literal 456 zcmZQzU_6nNoLkH!z`&53S5g$@?xYYA8KuDfh=G|wgn^TRk3lvuxwybL*e!$s2#OQS zi<3)=azJbZ_;!tf5yZQ!n~|88BJ|bG!?25MqR4tvt3>YCEC_X2S)3`U#mPXK4?rB3 zTu@ZPz`)1@Wb>4l6oA+gK(Nf0*3Z6ZY>LC!!m89;VIIubhx zi4C<2sDy#FG{6|B9sx>HD}eSfFgQCKFo41UBJ(*A>^DZSGYGNf`wR@W`$6&uF)tv0 zqd5Zu*H(m>h6MwIAQuC}iHhXXqB4ki3|c_ChT#X}9Hvs{W|os|ejGwv5ro~%u~m& Mr#>nF`1AjN0R3-a-2eap literal 0 HcmV?d00001 diff --git a/assets/profiles/icc/displayp3compat-v4.icc b/assets/profiles/icc/displayp3compat-v4.icc new file mode 100644 index 0000000000000000000000000000000000000000..0224e51d5500ea18e86760da12f8badc3e5bf3d2 GIT binary patch literal 480 zcmZQzV0@61oLkJIz`&53S5g$@?xYYA8KuDfh=G|wgn^TRk3lvuxwybL*e!$s2#OQS zi<3)=azJbZ_;!tf5yZQ!n~|88BG*30z3KeygJSuX3F}--f&j?%JgIr1!9bb=h-Dax83GuL8JuybVFRmiW^iUOU{C~w z7myDGh;S%QGd2PXG4ePuFtFbH|NpZp0|WbH28O>A{{R2I@c;jRehduEvlti-h#>Sg z-)CU3-4D`-5c2}^H<~jraBW41X;?5Y2y!tnoTw;BEJ_61!we*u(lQwsJ}qZp;EiNp K5WWD^6Ab`423#%x literal 0 HcmV?d00001 diff --git a/assets/profiles/icc/prophoto-v2-magic.icc b/assets/profiles/icc/prophoto-v2-magic.icc new file mode 100644 index 0000000000000000000000000000000000000000..be3a8b2950557f31881fbe27f44e17e49cc39fdc GIT binary patch literal 756 zcmZQzVEU4ioLkH!z`&53S5g$@?xYYA8KuDfh=G|wgn^TRk3lvuxwybL*e!$s2#OQS zi<3)=azJbZ_;!tf5yZQ!n~|88;(Pbh%DzcXOdR*-oJg)Z?}<={mBpEoTAU1&`2fW6 z$pu9v3=E7sKsHZ#Ndbs00c4950UZTo>wwtl5Viw|odjWn+$K^K666e2lL5p%=}7D( zBsSD8pmGM*(jb3d5CaBEQY(P=F)%ng8!&*v03!bF4%BZ}2(dHf3=E$8K=KGNrCAIN z+iZbCj0iCfRv;gUFY6?i7L|e21Hl0x7G=<8a9{{y$Yy9{SjBLIk%!Tlv4Qar6FXBN z(*mZ?%wEh(Sy)&yST3+yv#w^7XPd?@$Uc!nm}3^FD(7Y{SFYRKB|Jhrn|Q-`|MRWl zj}+h$I3!ptWGVDhc&|u{Xn>fk*n9D#5_2Rgr6Q!QWE5mMWxvWjmA|2IPVt1&QRO2l zCsfa=T~~ji@kNVWTUN(hH(0M&f11G_!>7iACJv^>W-H7eSjt#MThFn1Xs2mk>~PRY z$T`(zubY^AvBzaEGw<2HEPna^_W}cgj)Yi;ZVNY#*c@dRy(iW+?qWiG;>YCHRN1s` z86laUv!~_S_fB%0HaVMge#*rYm;YU}xKVs-)1A-v%^%i2KK@MTMZ&91Z&}|*f86qg|6A^l MbH5G#PXGTO0N}pocK`qY literal 0 HcmV?d00001 diff --git a/assets/profiles/icc/prophoto-v2-micro.icc b/assets/profiles/icc/prophoto-v2-micro.icc new file mode 100644 index 0000000000000000000000000000000000000000..1c92a174986b251a3e66e7bf3f3661d374a7d0db GIT binary patch literal 496 zcmZQzVEmAioLkH!z`&53S5g$@?xYYA8KuDfh=G|wgn^TRk3lvuxwybL*e!$s2#OQS zi<3)=azJbZ_;!tf5yZQ!n~|88^3U+~`u&H+C#NO~G^^B0E7iFqj;#j7rC^jH|p;Mnp!j3F!)p$;pHD)XOaZbZfNY&2paX&I7La&4ggpbqPJ*!4fY?PLLC!!m z2Y_sWbR>2X5<54i6l5<5fLza$nim=jq&a|ChQWxz0EkU+s9^)Eab|D^s!{}n7myDG zh;S%QGd2PXG4ePuFtFbH|NpZp0|WbH28O>A{{R2I@c;k6ix?P~?=UbN$V2E~lgYr~ sUhx0_|3-wERu%)pn#VxBYY}1!%nS_NYZ(|$|1U@^N(9>jBAC+B0Iw2U<^TWy literal 0 HcmV?d00001 diff --git a/assets/profiles/icc/rec2020-v2-magic.icc b/assets/profiles/icc/rec2020-v2-magic.icc new file mode 100644 index 0000000000000000000000000000000000000000..cd7074b554e699c22b2fa4fb05fd0200692fee1e GIT binary patch literal 790 zcmZQzU>3_s&MjsVU|`72D=7+ccT$Lmj8b5K#K6oT!obPE#~_=STwLHA>=wcR1jUKv z#mOZ_IUqIye7nZL2;yDV%}C5kIiDKBm$}`ldJ@mKbI%*(nGx!+vN%&xi<5yeAAmSM zxuB?ofq{_+$mS_8DFCr0fNYT>pre3n9S}Pm!gc_$lOSx6+eC^&f}DYBGJtqpIubhx zi4C<2sGNb-$iT<|#DIa4)C!<|3=GcB1`MDufQWw%1pAFq>y_SLD^#A12qB4ki3>Sg48$%DnB}NIxIL0N6ub2#&%9!>r zGckKG_c32#kzz?;S;q2?)tt4N^%9#BTPfRVb|v;2_M03Q95Xroawc=0<}&A6!Y#tx z&i$LGnCC5T7VitbEWUUArTqT{x&$Q!R|(k)T@lU|;SgCS>Mr_PY@)c8_*03=k}i@z zq}EBN%P7k{lwB;BCa)|1P2sTOB&B3!3l)BqSE|RoQ!_WS@U%>@skc)PHfc3KZk^X2*QwC;v}avk)dbH;(vv?-Jvn{p%$C{d zbA9HUE>v7BxRiO>&lR6myN+5CaBEQY(P=F)%ng8!&*v03!Z55bQTbu`>v{5Fa|R%W&XkH$99cl23J4NVSYow zJ0ge0Ur77NeNeip_Deg-fXC#QmA3sN*I4iPz~vF<@w{m|xl>AAYwcTG`h=&wTOho$ WZL`Z>|Klqz`Q3GT(e_2`|9=1)WoZup literal 0 HcmV?d00001 diff --git a/assets/profiles/icc/rec2020-v4.icc b/assets/profiles/icc/rec2020-v4.icc new file mode 100644 index 0000000000000000000000000000000000000000..8782122293de601abbccc274970be41797a09289 GIT binary patch literal 480 zcmZ{gze~eV5XZkUYW-2fq2QwM6kS9`>e5jfv6D+%rMjg_ZIvX%yhtYp#XmrB5eLD} z>F6kQcT}82;xFhfxM+FaONVsOgX4Yf`@Y8=2PmOs*u0+sw&Q#GT$Yu#cUa;TDpDB6 z7^ZZCyBme|A}F|C=Z5cDlH>S#gd(f^#hUJzORHBm?!!yi7~i^VpBx;;zCUTitZ;*V zekf-R*Yg2|5zo|pSMnNh%Bblkafx`rqXmgirC#mwh2(OV1Ib=7uaT!k%&I+H?qSK7e)zOfVqx IPG`9N1stAZe*gdg literal 0 HcmV?d00001 diff --git a/assets/profiles/icc/rec2020compat-v2-magic.icc b/assets/profiles/icc/rec2020compat-v2-magic.icc new file mode 100644 index 0000000000000000000000000000000000000000..d07462778d14bf5f79f3ff6f0ec4749f72315eac GIT binary patch literal 790 zcmZQzU>3_s&MjsVU|`72D=7+ccT$Lmj8b5K#K6oT!obPE#~_=STwLHA>=wcR1jUKv z#mOZ_IUqIye7nZL2;yDV%}C5kdBvjj{?tB$neQiVUH|rk^jw5GtSruy)Z%2I%m*Nj zPcA4bVPIh70kV0@OA0`22_Rdf2?8;q^1B+IV?D4a{T2?<~+@1&b5SFgu9*l zH%~FoTiz_*7kpWK@Aymk{|R&nN(!zLvK6`_oGZd1vP{%n^tISTaVzns5|breB!5V) zlTMdWmU$?klqCeB|is6Jn>F&y=29 zdEwz@nXA#)=iGdBN9$hMgQJfnp5{G2{z~Od%exmJ{XXyfruJjTZ|=V>|NjF3`Xcb| literal 0 HcmV?d00001 diff --git a/assets/profiles/icc/rec2020compat-v2-micro.icc b/assets/profiles/icc/rec2020compat-v2-micro.icc new file mode 100644 index 0000000000000000000000000000000000000000..f1e7a7b5a3b6607f6eed63c68067045aab3c76a5 GIT binary patch literal 460 zcmZQzU_6tPoLkH!z`&53S5g$@?xYYA8KuDfh=G|wgn^TRk3lvuxwybL*e!$s2#OQS zi<3)=azJbZ_;!tf5yZQ!n~|88q7i;#d;H5wuY7oZn^viQlR&7$%Hm8(Elvi?d;sG3 zCC zSo4^Hfqex+Oo5q!fqN|j!|B%K(xNhmc?>{5Fa|R%W&XkH$99cl23J4NVSYowJ0ge0 zUr77NeNeip_Deg-fXC#QmA3sN*I4iPz~vF<@w{m|xl>AAYwcTG`h=&wTOho$ZL`Z> S|Klqz`Q3GT(e_2`|9=3x8)j4h literal 0 HcmV?d00001 diff --git a/assets/profiles/icc/rec2020compat-v4.icc b/assets/profiles/icc/rec2020compat-v4.icc new file mode 100644 index 0000000000000000000000000000000000000000..2553b3ccb9b4f477af6650e2d80f98a419b560a2 GIT binary patch literal 480 zcmZQzV0@61oLkJIz`&53S5g$@?xYYA8KuDfh=G|wgn^TRk3lvuxwybL*e!$s2#OQS zi<3)=azJbZ_;!tf5yZQ!n~|88^3r@yuku|j(WuRCTaMfbyNOVTmBp2kTAU1&`2)l% z$pu9v3=E74K(!OI0IEFg2D^P z2LePm6sH**frS`(oER8b@BRP(S(Sl-eKG^X-wFT!|6ch2|Gydr247iFqmgd*fODygvW;QQf`wo|i=*Bh+DKai*jeCj(_Z0C9YB zK~V_<10xTR%~M`d0Afo3*&;hx5(9(90R{%U@%^EeEU*NH*cFDG7Usjr~u3md=gUF`zE&H}h?yTH>cc1HlO^20_&Nv}- yYQkBe^D{50T-kix{pS5UHTPv69(t1fT=M0aH_h+uKK}c1=zG_%@V}b>{{sMm>fAd3 literal 0 HcmV?d00001 diff --git a/assets/profiles/icc/rec601ntsc-v2-micro.icc b/assets/profiles/icc/rec601ntsc-v2-micro.icc new file mode 100644 index 0000000000000000000000000000000000000000..963b14932761980182fa5bb8c42437039cc3cb02 GIT binary patch literal 460 zcmZQzU_6tPoLkH!z`&53S5g$@?xYYA8KuDfl!1jont=<584{C=3w(p!LKuLcII+Aq zxuhru#72N`*BBT7iFqmid(O@&;?*`%l3pl&>!$czggUG&&Xm;RWT4CkAdXKi zC@NuKVB`U^dCE%)Kx_#hTcil+C?H!0#7>8>9YE|P2pi-!k)n_wXP}x4AUh=;iJgSR zhS~*G!oXT;W?%?nz(7fA1<*bQ24`mj22dD4#6Jgu{l+ME1|gO;iGji500RSa9YQQ+ zB?H42E(QkiM1+|7Yz78lGX{noe#xapWf1cifPP>MW?IJljn#|o62~O2E}nh-+JaX_ z_J}`}c9nanbXM(?cBBD|$ww7iFqj@W>1P=tW&m&a{Qx}XxGw;P=}Spm6BSV43zl;#45=J zMI{Ujj0!-uQh7-Mi0uMoizH_xrU2OyK(1Fat>@i>C|>FFpX(3INS{ I0t|+|0PhoCBme*a literal 0 HcmV?d00001 diff --git a/assets/profiles/icc/rec601pal-v2-magic.icc b/assets/profiles/icc/rec601pal-v2-magic.icc new file mode 100644 index 0000000000000000000000000000000000000000..69e4c2ffc81a8858ec110cc2d89af7d99f76c2ac GIT binary patch literal 738 zcmZQzV0x63oLkH!z`&53S5g$@?xYYA8KuDfl!1jont=<584{C=3w(p!LKuLcII+Aq zxuhru#72N`*BBT7iFql8Qm+&?O!8LXT7PV#f$VjJGVCnQl+@y6pwtH-j!!Nq zDq&z?mI%1a7BYzZJ+qzLFJAX^8-PKU4^Kr6b2CS&w*gSF^Zi*h?R6RFj$u}Ffi{xh{XXz zYO4tYgVargn2G@d1Aiz3!UcW3?CRB7$-13U@~B8V7kDpz+Aw5 zfJJ~Mk!3B*Kh_Y|HEe8bscgsCwb&=I|KZ5uxXtO$d5p`EYd^O=_Yod%o~yhmykGgc z_?7tg3d9Kf6I>$XBlJUfrAV}>kmwn)E^%)Oeu*2Bi=~RBU1g+YzRF&b+afCBcMdol;-W$W+OClCv_eu)wZ}ulPynp7NQM)z$H}UiD^;n$2>p67Axh65R^DTK$$2eI_SQ zt)D)B=AqfI=ZP=ySX8@Y+p;e!O;=a1J+?t)Q~H*D+a-5a?!LRv^}wdX%139M5IQyC vtkC(HmsGB7zV3eW{+*ipG7k?u$$l>R^30p&_jVuueL3{K>sR<+&Hw)ans?Xi literal 0 HcmV?d00001 diff --git a/assets/profiles/icc/rec601pal-v2-micro.icc b/assets/profiles/icc/rec601pal-v2-micro.icc new file mode 100644 index 0000000000000000000000000000000000000000..af194eda132679c7e06e208f77cb9744d4c9fd04 GIT binary patch literal 460 zcmZQzU_6tPoLkH!z`&53S5g$@?xYYA8KuDfl!1jont=<584{C=3w(p!LKuLcII+Aq zxuhru#72N`*BBT7iFqlFhdI6od7X*ck`*4w`L=N(LLF8XXG&^uGEn9N5XUDM z6qPVAF!BJ|Jmn%11Jn2;-3S-eq$6ngAgm}W?--`XJBC7fe?!W zhSXLQ1_r5{2r(4{1_u6628Jzjl1q!qAm%Xu{lFN^w2b*1s~6iPj!9fyJp1^y1+R+i z5q~J{D)&<9tlB5-NCOs=k5Pu!(ST&w literal 0 HcmV?d00001 diff --git a/assets/profiles/icc/rec601pal-v4.icc b/assets/profiles/icc/rec601pal-v4.icc new file mode 100644 index 0000000000000000000000000000000000000000..a3cf0b29b7ea013f3b230374fd85b0ea23c51c02 GIT binary patch literal 480 zcmZQzV0@61oLkJIz`&53S5g$@?xYYA8KuDfl!1jont=<584{C=3w(p!LKuLcII+Aq zxuhru#72N`*BBT7iFqjo?=z=AP`f>;aq5L6jy<+u8H8F47FSAYaWYW!4-l&) z7ZjB+Ffb|r*-GUl1t7KykS&s&k(dHxM*!J6ML-7v*)1UPbO?I}h@AvsuK}@(LV}!u zY7PL|3h7AfBqVljPASM<5CFN6Cp9lL7)Wyfu?&M5g8_pfLjVpnY+yCc49-ARilFcU z@__&m4#jE4MqnXE9w!C{)_ec|e^zB+V4uvu@OQ%h|GyXh|NnOp0|WCN28IK92>m79 z3=G!gK=B<2u{dB1Y&BtEkh+NwQ!!v*;16YB*fOUeu_zI24>OQtvUtkC@ZtjlgSY?# LgT)hIFzf{Y)iYkb literal 0 HcmV?d00001 diff --git a/assets/profiles/icc/rec709-v2-magic.icc b/assets/profiles/icc/rec709-v2-magic.icc new file mode 100644 index 0000000000000000000000000000000000000000..5a456237ed615115c148e2616aa351db5344600f GIT binary patch literal 738 zcmZQzV0x63oLkH!z`&53S5g$@?xYYA8KuDfh=G|wgn^TRk3lvuxwybL*e!$s2#OQS zi<3)=azJbZ_;!tf5yZQ!n~|885`1gIijpHjhc?UJSv7y<{5c49SXrDYsl~}anGZl5 zpIlH>!oa}D17!1*mlS~55{5ztXUwhoA$4q-ch*hvsJ$ZaA;AwkYSH5ou$m5#(t zLSjSh0xD-<4Kg>d1TkQsB((x)9|ME4vjGDr3?Sm41Hpb{6gz_u%U{62VDXiKfq4Q# zENMCe!}fLt28mk;F_rlY4E!w&4BHMTmll;l%wyONqyrchFnnNiV4T4CfXRTVf$0LX z0&@ZL0Tuz4M3%KI|5!s<*RZj%rLrAk*J7W<{)Z!r<2I*1=P@ovuKnEh+(&r4d9L!N z@P6g%;#cC|D-a{_PjHElkI)a{l_JriLZWBHy2QOD_$6*gE|w~ic9oHq`6_!!Zj1a3 zg$BhOrD$b;6<1XUH9K{C4OdM+t!V8WoqF9FdRz1_8+_lvqI-*UQ)TT`MUef`*&*Y%RD^vB>TDK%QJ79-`jos_vO&{u3zDQHUIwy08L5U A?EnA( literal 0 HcmV?d00001 diff --git a/assets/profiles/icc/rec709-v2-micro.icc b/assets/profiles/icc/rec709-v2-micro.icc new file mode 100644 index 0000000000000000000000000000000000000000..569cb4b67aec8aeaa005b5ea2c3ef18da068408f GIT binary patch literal 460 zcmZQzU_6tPoLkH!z`&53S5g$@?xYYA8KuDfh=G|wgn^TRk3lvuxwybL*e!$s2#OQS zi<3)=azJbZ_;!tf5yZQ!n~|88GUaQrQ(M!2-?HEdlk*i;T|}tE%Hm8(Elvi?d;sG3 z{5Fa|R%WB$hK#de8f5?2?`K7MV% zt0H^EA4(fD%i@skL3SXo>tsl~}anLj|R zl3Y+!!oa|&0AwqbmlS~5El6VU2xPZ_#M2?{86b8NguMpDE(!^9 z2C6v#WGkd2v6GP4xjCgEdqDu?dY;t0&|o0V0mL#4K@8>$1`L)s)UbioI5Ri{)hL3( z3&;loL^u?u85@Cx7_zD!CfDlWX&cLv}oq<8(7D7yAJ_7?k(Er;G7bF%Xg6&}jl1vs)85mxCU|zR6<| literal 0 HcmV?d00001 diff --git a/assets/profiles/icc/scrgb-v2.icc b/assets/profiles/icc/scrgb-v2.icc new file mode 100644 index 0000000000000000000000000000000000000000..47b66d3d108abaacae94ce5d1f03518e72f7e273 GIT binary patch literal 372 zcmZQzU@XZ=&MjsVU|`72D=7+ccT$Lmj8b5K%)r4Q!oa|w!XTQMTwLHA>=pv#F%&13 z7blk#<$%}-@a-A{BZzldHzP4G<)Nil=!SID+ZTP7_XINa_#o6_WpSpY7AFH`J^*oi zazRlE0|O%ukj+zGQUGF00NEl%Kt}=DIv{pBgzW%gCqdXCw}}*m1UUoMWB}Pb=}7D( zBsSD8pb`ewWT0n33>YX$tpM7`z~Jm`zyJyZi1_Cau-_QPP9nte7cekbd}3f=?nj6v hO=Do#-pRlqaSI`)0#wJ}%D}MgNOEaW8N@sW1^^{CL7@Nu literal 0 HcmV?d00001 diff --git a/assets/profiles/icc/sgrey-v2-magic.icc b/assets/profiles/icc/sgrey-v2-magic.icc new file mode 100644 index 0000000000000000000000000000000000000000..1ea54310ec58bfcf955e1a5f9fb701c49500979a GIT binary patch literal 616 zcmZQzV9Llz&MjsVU|`72D=Bgha*T|Kj8b5K#K6oT!obPE#~_=STwLHA>=wcR1jUKv z#mOZ_IUqIye7nZL2;yDV%}C5k={=?M-A{Y(l?yN1t<2Rvb0gGYWwE5B7AFH`wg7Q_ zazRlEkiG!KJmnYP{9r($>}q)UDB5rGL**%qYlsipfng74u?? zV^#{*H8%I{TF@Pp6^;jWPkQTt+A;(QXM z6W=EvPhFPYnOT?}pBtF(Rp?giRT@|xS6NuyQM;`EMB|4Rxwe3g&aR`qoc)0l=TH7T z&3ne`*?e;==09H)z2w^Ru$8yhq_6wFv44x!wlh0R_9*VVda(D1&vAj1x6Z6SUw=wcR1jUKv z#mOZ_IUqIye7nZL2;yDV%}C5kVZFq({O4WwDaZYN;?7NM8VAp7N3c5c>`gi)4ocIRohrK%4+o4`ed1mbw>Jf*3GRl3D@M!{F>}zyR_G zMEr9I*nNy*CzDHy${=D4T0pvn;RoX!rc&l+mXmCL970?YJV|_C1ka0nlgN_MQ*c&U zqnV{wXZ+f7v;8%d}v literal 0 HcmV?d00001 diff --git a/assets/profiles/icc/sgrey-v2-nano.icc b/assets/profiles/icc/sgrey-v2-nano.icc new file mode 100644 index 0000000000000000000000000000000000000000..0e9edfd403182dd3ca815935cc85f33ec5dbd746 GIT binary patch literal 290 zcmZQzU{uOU&MjsVU|`72D=Bgha*T|Kj8b5K#K6oT!obPE#~_=STwLHA>=wcR1jUKv z#mOZ_IUqIye7nZL2;yDV%}C5k;hV$Xm^N{`e8QEs#kOw0V-V`FvRG15i<5yeTYxye zyriH6NM8VAk?fElXCVCqh)t3Uih$yb5b%7 literal 0 HcmV?d00001 diff --git a/assets/profiles/icc/sgrey-v4.icc b/assets/profiles/icc/sgrey-v4.icc new file mode 100644 index 0000000000000000000000000000000000000000..c77088231973f02a5c3326bae5c9fb197ac82d0c GIT binary patch literal 360 zcmZQzV9dx#&Mjt9U|`72D=Bgha*T|Kj8b5K#K6oT!obPE#~_=STwLHA>=wcR1jUKv z#mOZ_IUqIye7nZL2;yDV%}C5kS-oO|!!nIV>3)5yoHwkpcM7NIwB$rSg&jAe)f`$QDV?NK65;Wq@p*?2sU5Aln1TR>;jM1?dF=kPCQH^Fo7x zG)RvOLotIpLlHwI4mE6GHO>sq319<^vGN zCl?fzFfcIk0NFg{B?Tb11duIK1auUTtpj4GL)Z==b`pdQa+^p|NRTs7O$HEGq$9DD zkl0YWfXW$Ii-Dd6F<_u1wE}1#1B0`(0Rt!uAmX1xzU%w5_dnW^f#4UuF3Q!$?D+9x}Bgv&jWf1ciwgG7w1`CD|h608jh7}A)7#=b*Fe)*+ zG3GK(Wjx6Efk}ZWl&PEP2s0zIJ#!QDF%~|SNS5U+KUsZPm$3e0i(%WxuE^fS{)Ho% z<0_{o=V2~;uKnCj+$VWLcpmbW@Nx1j;&e!5TP-eaZJj{f8ogEe_YB31 zf{dq_+%!`$FSa;lrC?oSbKlO@euJZ;(=-Q*XRMyhH@9N`^F`51t}PE+d3#Ozy6+qNw`grUv$JH6;=Zc~dyn`W v7dUzA%pre3n9S}Pm!gc_$lOSx6+eC^&f}DYBGJx!abR>2X z5*un4PzeKTDbTYZ1`L#>Rsii|U~qOeU;u>yMEr9I*l&ztClO-#3m6zIJ~1#b_anrT zrZF&V?_^+*xP=f?0jlF~WnkELB)PPx3}POG7LcxC_`x`bsg${yDB(h}m6r5GoXlCiv8NasNY=6xy+}A9mEJi#s(?(@Q&zoQZDh!V&7QvN%&xi<5yeAAmT% zyriInfq{_+$QCIAIt0j80I|~{Y!eVW3BvXOv5P{2oPla$fNYa=Bz6)KJGr0;Wab2* z8ZM|^Kqdog9?-KO1`L4w4Pk!{0sDth>?A@ge*pu7#U};^=6-}&(liE!?VSt^61Nay zDnNDotqcs?jwF{Bl|jq{h9m19<_{37 zBo`EwFfcGG0NG0AB?Tb13y>|6oROFUWJdtmIz>PS0@*Df@pK4#28f*mVXpzPi$a2& zfocu_*$U}M>?9<1ZcZu4UJwAeo+mXgG#E&80I>{1F+&i8JA)GrHEdut&J4~B1`LXz z@B;FI01*zwX~sriAx0j11_su<|NnnhV_;yPz`*c#(*OUz7ykeMcM$^v^Bo3;1BD3v z`3o2rEIt9n`w?PE(-;`GcQP=wcR1jUKv z#mOZ_IUqIye7nZL2;yDV%}C5k$*=vb+SOymc=?Wsr*`V(NeFdVS)3`U#mPXK4?rBB zTu@ZPz`)1@Wb>4l6oA+gK(Nf0*3Z6ZY>LC!!m89+8)Iubhx zi4C<2sDy#lG29)f9sx>HD}eSfFgQCKFo41UBJ=GI)NfV@vF%w53{KV{d4!ncZ3c#& g?F7iFqmgdYtmx7jNGCIb!yA?b~zSBGh4JaiyddCj({v0I^DP zK~V_<1ET_vtyEr80AjlU*&@jqi77yK1dy#$1au&f-2xI%hp=aW*hvuf8W6iEB*+=4 z<^Yf_kdDMoLSpCUl!EL90g&r?Qu9KCfiwpY%P=@HgfqA^IO9;m23F(D;LKpapa=>t zARh=2;ZU4rYy=Ve|M&m@|No%)FVH4tAR7#T@<4n4zenibp2fi6WDU}X5R<&kz_7EO ffq_>BA*MQ!fq})2f#H~7L1IxN#2z5YWNZuos>NZ` literal 0 HcmV?d00001 diff --git a/internal/thumb/README.md b/internal/thumb/README.md new file mode 100644 index 000000000..ade1d44d1 --- /dev/null +++ b/internal/thumb/README.md @@ -0,0 +1,65 @@ +## PhotoPrism — Thumbnails Package + +**Last Updated:** November 23, 2025 + +### Overview + +`internal/thumb` builds thumbnails with libvips, handling resize/crop options, color management, metadata stripping, and format export (JPEG/PNG). It is used by PhotoPrism’s workers and CLI to generate cached thumbs consistently. + +### Context & Constraints + +- Uses libvips via govips; initialization is centralized in `VipsInit`. +- Works on files or in-memory buffers; writes outputs with `fs.ModeFile`. +- ICC handling: if a JPEG lacks an embedded profile but sets EXIF `InteroperabilityIndex` (`R03`/Adobe RGB, `R98`/sRGB, `THM`/thumbnail), we embed an Adobe-compatible profile; otherwise we leave color untouched. +- Metadata is removed from outputs to keep thumbs small. + +### Goals + +- Produce consistent thumbnails for all configured sizes and resample modes. +- Preserve color fidelity when cameras signal color space through EXIF interop tags. +- Keep error paths non-fatal: invalid sizes, missing files, or absent profiles should return errors (not panics). + +### Non-Goals + +- Serving or caching thumbnails (handled elsewhere). +- Full ICC workflow management; only minimal embedding for interop-index cases. + +### Package Layout (Code Map) + +- `vips.go` — main `Vips` entry: load, resize/crop, strip metadata, export. +- `vips_icc.go` — EXIF InteroperabilityIndex handling and ICC embedding. +- `icc.go` — lists bundled ICC filenames (`IccProfiles`) and `GetIccProfile` helper. +- `resample.go`, `sizes.go` — resample options and predefined sizes. +- `thumb.go` and helpers — naming, caching, file info. +- Tests live alongside sources (`*_test.go`, fixtures under `testdata/`). + +### ICC & Interop Handling + +- EXIF `InteroperabilityIndex` codes we honor (per EXIF TagNames and regex.info): + - `R03` → Adobe RGB (1998) compatible (`a98.icc`, etc.) + - `R98` → sRGB (assumed default; no embed) + - `THM` → Thumbnail (treated as sRGB; no embed) +- If an ICC profile already exists, we skip embedding. +- Test Files: + - `testdata/interop_index.jpg` — R03 interop tag, no ICC (expects Adobe profile embed). + - `testdata/interop_index_srgb_icc.jpg` — R03 tag with embedded ICC (must remain unchanged). + - `testdata/interop_index_r98.jpg` — R98 interop tag, no ICC (should stay sRGB without embedding). + - `testdata/interop_index_thm.jpg` — THM interop tag, no ICC (thumbnail; should remain unchanged). +- References: + - [EXIF TagNames (InteroperabilityIndex)](https://unpkg.com/exiftool-vendored.pl@10.50.0/bin/html/TagNames/EXIF.html) + - [Digital-Image Color Spaces: Recommendations and Links](https://regex.info/blog/photo-tech/color-spaces-page7) + +### Tests + +- Fast scoped: `go test ./internal/thumb -run 'Icc|Vips' -count=1` +- Full: `go test ./internal/thumb -count=1` + +### Lint & Formatting + +- Format: `make fmt-go` +- Lint: `make lint-go` or `golangci-lint run ./internal/thumb/...` + +### Notes + +- When adding ICC files, place them in `assets/profiles/icc/` and append to `IccProfiles`. +- Comments for exported identifiers must start with the identifier name (Go style). diff --git a/internal/thumb/icc.go b/internal/thumb/icc.go index e567aa430..b733e02f3 100644 --- a/internal/thumb/icc.go +++ b/internal/thumb/icc.go @@ -1,21 +1,189 @@ package thumb import ( + "errors" + "fmt" "os" - "path" + "path/filepath" + "strings" ) -/* -Possible TODO: move this into a shared pkg/ so non-thumb -consumers can also use it. However, it looks fiddly to hook that -up to `assets`, so I'm punting on that for now. -*/ +// Standard ICC profiles located in "assets/profiles/icc". +const ( + // IccAdobeRGBCompat is compatible with Adobe RGB (1998). + IccAdobeRGBCompat = "a98.icc" -func MustGetAdobeRGB1998Path() string { - p := path.Join(IccProfilesPath, "adobe_rgb_compat.icc") - _, err := os.Stat(p) - if err != nil { - panic(err) - } - return p + // IccAdobeRGBCompatV2 is A98C (Adobe RGB 1998 compatible, ICC v2). + IccAdobeRGBCompatV2 = "adobecompat-v2.icc" + // IccAdobeRGBCompatV4 is A98C (Adobe RGB 1998 compatible, ICC v4). + IccAdobeRGBCompatV4 = "adobecompat-v4.icc" + + // IccAppleCompatV2 is APLC (Apple Color Matching compatible, ICC v2). + IccAppleCompatV2 = "applecompat-v2.icc" + // IccAppleCompatV4 is APLC (Apple Color Matching compatible, ICC v4). + IccAppleCompatV4 = "applecompat-v4.icc" + + // IccCgats001CompatV2Micro is uCMY (CGATS.001 compatible CMY, compact). + IccCgats001CompatV2Micro = "cgats001compat-v2-micro.icc" + + // IccColorMatchCompatV2 is ACMC (ColorMatch RGB compatible, ICC v2). + IccColorMatchCompatV2 = "colormatchcompat-v2.icc" + // IccColorMatchCompatV4 is ACMC (ColorMatch RGB compatible, ICC v4). + IccColorMatchCompatV4 = "colormatchcompat-v4.icc" + + // IccDciP3V4 is TP3 (DCI‑P3). + IccDciP3V4 = "dci-p3-v4.icc" + + // IccDisplayP3V2Magic is sP3 (Display P3, ICC v2 magic). + IccDisplayP3V2Magic = "displayp3-v2-magic.icc" + // IccDisplayP3V2Micro is uP3 (Display P3, micro). + IccDisplayP3V2Micro = "displayp3-v2-micro.icc" + // IccDisplayP3V4 is sP3 (Display P3, ICC v4). + IccDisplayP3V4 = "displayp3-v4.icc" + + // IccDisplayP3CompatV2Magic is sP3C (Display P3 compatible, ICC v2 magic). + IccDisplayP3CompatV2Magic = "displayp3compat-v2-magic.icc" + // IccDisplayP3CompatV2Micro is uP3C (Display P3 compatible, micro). + IccDisplayP3CompatV2Micro = "displayp3compat-v2-micro.icc" + // IccDisplayP3CompatV4 is sP3C (Display P3 compatible, ICC v4). + IccDisplayP3CompatV4 = "displayp3compat-v4.icc" + + // IccProPhotoV2Magic is uROM (ProPhoto RGB compact). + IccProPhotoV2Magic = "prophoto-v2-magic.icc" + // IccProPhotoV2Micro is uROM (ProPhoto RGB micro). + IccProPhotoV2Micro = "prophoto-v2-micro.icc" + // IccProPhotoV4 is ROMM (ProPhoto/ROMM RGB, ICC v4). + IccProPhotoV4 = "prophoto-v4.icc" + + // IccRec2020Gamma24V4 is 2024 (Rec.2020 gamma 2.4, ICC v4). + IccRec2020Gamma24V4 = "rec2020-g24-v4.icc" + // IccRec2020V2Magic is 2020 (Rec.2020, ICC v2 magic). + IccRec2020V2Magic = "rec2020-v2-magic.icc" + // IccRec2020V2Micro is u202 (Rec.2020 micro). + IccRec2020V2Micro = "rec2020-v2-micro.icc" + // IccRec2020V4 is 2020 (Rec.2020, ICC v4). + IccRec2020V4 = "rec2020-v4.icc" + + // IccRec2020CompatV2Magic is 202C (Rec.2020 compatible, ICC v2 magic). + IccRec2020CompatV2Magic = "rec2020compat-v2-magic.icc" + // IccRec2020CompatV2Micro is u20C (Rec.2020 compatible, micro). + IccRec2020CompatV2Micro = "rec2020compat-v2-micro.icc" + // IccRec2020CompatV4 is 202C (Rec.2020 compatible, ICC v4). + IccRec2020CompatV4 = "rec2020compat-v4.icc" + + // IccRec601NtscV2Magic is R601 (Rec.601 NTSC, ICC v2 magic). + IccRec601NtscV2Magic = "rec601ntsc-v2-magic.icc" + // IccRec601NtscV2Micro is u601 (Rec.601 NTSC, micro). + IccRec601NtscV2Micro = "rec601ntsc-v2-micro.icc" + // IccRec601NtscV4 is R601 (Rec.601 NTSC, ICC v4). + IccRec601NtscV4 = "rec601ntsc-v4.icc" + + // IccRec601PalV2Magic is 601P (Rec.601 PAL, ICC v2 magic). + IccRec601PalV2Magic = "rec601pal-v2-magic.icc" + // IccRec601PalV2Micro is u60P (Rec.601 PAL, micro). + IccRec601PalV2Micro = "rec601pal-v2-micro.icc" + // IccRec601PalV4 is 601P (Rec.601 PAL, ICC v4). + IccRec601PalV4 = "rec601pal-v4.icc" + + // IccRec709V2Magic is R709 (Rec.709, ICC v2 magic). + IccRec709V2Magic = "rec709-v2-magic.icc" + // IccRec709V2Micro is u709 (Rec.709, micro). + IccRec709V2Micro = "rec709-v2-micro.icc" + // IccRec709V4 is R709 (Rec.709, ICC v4). + IccRec709V4 = "rec709-v4.icc" + + // IccScRgbV2 is cRGB (scRGB, ICC v2). + IccScRgbV2 = "scrgb-v2.icc" + + // IccSGreyV2Magic is sGry (Display P3 compatible gray, ICC v2 magic). + IccSGreyV2Magic = "sgrey-v2-magic.icc" + // IccSGreyV2Micro is uGry (Display P3 compatible gray, micro). + IccSGreyV2Micro = "sgrey-v2-micro.icc" + // IccSGreyV2Nano is nGry (Display P3 compatible gray, nano). + IccSGreyV2Nano = "sgrey-v2-nano.icc" + // IccSGreyV4 is sGry (Display P3 compatible gray, ICC v4). + IccSGreyV4 = "sgrey-v4.icc" + + // IccSRgbV2Magic is sRGB (standard sRGB, ICC v2 magic). + IccSRgbV2Magic = "srgb-v2-magic.icc" + // IccSRgbV2Micro is uRGB (sRGB micro). + IccSRgbV2Micro = "srgb-v2-micro.icc" + // IccSRgbV2Nano is nRGB (sRGB nano). + IccSRgbV2Nano = "srgb-v2-nano.icc" + // IccSRgbV4 is sRGB (standard sRGB, ICC v4). + IccSRgbV4 = "srgb-v4.icc" + + // IccWideGamutCompatV2 is AWGC (Adobe Wide Gamut compatible, ICC v2). + IccWideGamutCompatV2 = "widegamutcompat-v2.icc" + // IccWideGamutCompatV4 is AWGC (Adobe Wide Gamut compatible, ICC v4). + IccWideGamutCompatV4 = "widegamutcompat-v4.icc" +) + +// IccProfiles lists all bundled ICC profile filenames in one place so tests and +// callers can iterate or validate the full set shipped in assets/profiles/icc. +var IccProfiles = []string{ + IccAdobeRGBCompat, + IccAdobeRGBCompatV2, + IccAdobeRGBCompatV4, + IccAppleCompatV2, + IccAppleCompatV4, + IccCgats001CompatV2Micro, + IccColorMatchCompatV2, + IccColorMatchCompatV4, + IccDciP3V4, + IccDisplayP3V2Magic, + IccDisplayP3V2Micro, + IccDisplayP3V4, + IccDisplayP3CompatV2Magic, + IccDisplayP3CompatV2Micro, + IccDisplayP3CompatV4, + IccProPhotoV2Magic, + IccProPhotoV2Micro, + IccProPhotoV4, + IccRec2020Gamma24V4, + IccRec2020V2Magic, + IccRec2020V2Micro, + IccRec2020V4, + IccRec2020CompatV2Magic, + IccRec2020CompatV2Micro, + IccRec2020CompatV4, + IccRec601NtscV2Magic, + IccRec601NtscV2Micro, + IccRec601NtscV4, + IccRec601PalV2Magic, + IccRec601PalV2Micro, + IccRec601PalV4, + IccRec709V2Magic, + IccRec709V2Micro, + IccRec709V4, + IccScRgbV2, + IccSGreyV2Magic, + IccSGreyV2Micro, + IccSGreyV2Nano, + IccSGreyV4, + IccSRgbV2Magic, + IccSRgbV2Micro, + IccSRgbV2Nano, + IccSRgbV4, + IccWideGamutCompatV2, + IccWideGamutCompatV4, +} + +// GetIccProfile returns the absolute path to the first requested ICC profile +// that is present in assets/profiles/icc. It validates existence so callers +// can embed profiles without risking a panic or missing file error. +func GetIccProfile(profiles ...string) (string, error) { + if len(profiles) == 0 { + return "", errors.New("no icc profiles specified") + } + + // Find first ICC profile file that exists. + for _, p := range profiles { + filePath := filepath.Join(IccProfilesPath, p) + if info, err := os.Stat(filePath); err == nil && !info.IsDir() { + return filePath, nil + } + } + + return "", fmt.Errorf("no matching icc profiles found (%s)", strings.Join(profiles, ", ")) } diff --git a/internal/thumb/icc_test.go b/internal/thumb/icc_test.go new file mode 100644 index 000000000..a73142dc9 --- /dev/null +++ b/internal/thumb/icc_test.go @@ -0,0 +1,55 @@ +package thumb + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/photoprism/photoprism/pkg/fs" +) + +func TestGetIccProfile(t *testing.T) { + t.Run("Exists", func(t *testing.T) { + profilePath, err := GetIccProfile(IccAdobeRGBCompat) + require.NoError(t, err) + assert.True(t, fs.FileExists(profilePath)) + }) + t.Run("Missing", func(t *testing.T) { + originalPath := IccProfilesPath + missingDir := t.TempDir() + + IccProfilesPath = missingDir + + t.Cleanup(func() { + IccProfilesPath = originalPath + }) + + profilePath, err := GetIccProfile(IccAdobeRGBCompat) + + assert.Error(t, err) + assert.Empty(t, profilePath) + }) +} + +func TestIccProfiles(t *testing.T) { + for _, profile := range IccProfiles { + t.Run(profile, func(t *testing.T) { + path, err := GetIccProfile(profile) + + require.NoError(t, err) + require.True(t, fs.FileExists(path)) + + //nolint:gosec // test-only: path is constrained to known profile files in assets + data, err := os.ReadFile(path) + require.NoError(t, err) + + if len(data) < 40 { + t.Fatalf("profile %s too small to contain ICC header", profile) + } + + assert.Equal(t, []byte{'a', 'c', 's', 'p'}, data[36:40], "profile %s must contain ICC signature", profile) + }) + } +} diff --git a/internal/thumb/testdata/interop_index_r98.jpg b/internal/thumb/testdata/interop_index_r98.jpg new file mode 100644 index 0000000000000000000000000000000000000000..93c7e6d505f9fd9af975a63bafa674f51a9380f9 GIT binary patch literal 540666 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

    4OlVQdORm37~tH!9&Gtw*y$+hjlDC=m5IOU`?( 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
      1>RL}n#jJkUI6KqsvzH>= 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

    *{aojslV@AB!0KkCGeh>i_J+KV{f^oq72 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Μ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