From 6bc87cc0f5c9a05b1a07c117f100e53e6ff68935 Mon Sep 17 00:00:00 2001 From: Broque Thomas Date: Tue, 29 Jul 2025 14:10:06 -0700 Subject: [PATCH] improved metadata speed --- core/__pycache__/plex_client.cpython-310.pyc | Bin 20537 -> 20565 bytes core/plex_client.py | 22 +++ .../__pycache__/dashboard.cpython-310.pyc | Bin 0 -> 46243 bytes .../__pycache__/dashboard.cpython-312.pyc | Bin 76103 -> 82585 bytes ui/pages/dashboard.py | 161 ++++++++++++++++++ 5 files changed, 183 insertions(+) create mode 100644 ui/pages/__pycache__/dashboard.cpython-310.pyc diff --git a/core/__pycache__/plex_client.cpython-310.pyc b/core/__pycache__/plex_client.cpython-310.pyc index ec9bf167e841caddedf9d3acf657891c04c55e24..4573ab7d109140f37f7e359408fc704288cafd03 100644 GIT binary patch delta 2522 zcmZWqYj7LY72dnMTCG;nN|7It9ovzU5TnGlocM{cNgPuGA#rfy)RPeKD7kA#iEOJ^ z>jY_5Rv`>Srj*Id%?t$^x^6O*l)^MsAJDYnQD_P?{gJ)^GcfQg=}aF3v@nwa+HF(L@9G&l+bMHRylE1jboDK#J2|k}}Jbv(rd(WAZDyb-aPyIw8SNKW&r(_G? z>YFY$Su4h6x{j{DU)@_zqgIqER?upwk;YZpOk2RZa>1&CrIjYY5)+m-s}i{CxJEb7 zjbLqD!fU4;U|B^wtpM$^s;z*vdR(rN7Wmz?2cE8>gY?GjK%+5}B^x!)rH?F8(mqEo0a_B+U3Pc$9ICTZ-A4J25sH(9V3rR zw9Y}ScVxhZQ(g?__(ma|D%s%^BzKHR1(Qhm2Jl-(R|M$x|V(XftE%R z=1;YJ$IJGBO7;Zb-nx-Y@nY++T&`&3h)SDQ5BaH&pSbyFz?(hem!<+S-vI>#s%< z>C}FfWVytoWhZIUPA0DEiOh7?wpjM6k(x|SScyX!YvL@Q>#Qed`3s%TMSg}sjhRe( zI!VW7Sel*Z$Gf`8tNf*|Tlx1pYP2VTkZ!fu4+0Q3G#l37#sNNSy(w%91zcLBt`Y%8v7 z5U{T7WrXtxzr~hTRfw#tF_FkvS?SwQ>8MgiH2z{=G_aJwNq1$14&Hc548oN~nfcZH z`tH?tH9D%RP$+G|W2>&m@lem4T+kd1ryUxjiNw`b#SGsvnFUyO5LGrv00ds{=`X5azous|+~k9{BCA z1u`mz(NaNy$5FnbK$%sjS15p$7%e^DHh9oFa5$D4k0qI%%Gxo0{)R>k6|sx_@(r6B zU&E#7py+WHZ?VU5bwMoO-kUHr85H5Mg^G;Qw|h^Lsy`w1%TDYP@90}Y{>=CE-3CN{ zrf;N(2i-Nj*F$CU_W$XX3rblttD#B()VqW#RGyPvc}{|Qs0GzlT-8yj?8tM9uzGBd ztIbKS7y3@1@1BC!QCJW5m1?3kf>#7Hn!I8smR)fd>ok%QV~`?e$isN(`KXch6?8{; z4eG}o>k!wE$KR54$Dlnv$Hy2AIKCqiV|D;`dl2H`;VGB*IfWw<1~4Sr*B{Dp=$|3_ z7jVNgTqe_H4jd3mjs~M(%o^xK#Y*(Af}g&rL%WED{v!`UY0D#YXoAOJU4ZForLt_gbF*`MBW$ol-CWZ&Z$SwI5UpYP& ziTJpUF$l@x0P$rEM`zy4tV}w2z+$KH$?IrRvNQV`4h#D$e`a$#-`5o)e*T}$aqQQuirZ9t4Pc${Bp&c9u8p=p>i;=#Fh+(Q5(!KyDL1x8v~G z579S>@MC~^>0F{(CXQrv#l(91r5JL5FNOLE3s4G!qsI26XEX&BB{@L zzaHfe5BW$N|L#y-#lDDHZAPlh>gwu9RkabRZD{AO4($zmgdSc5iSYG14~NBzLj21z Lw7$-t+{yn38Znj( delta 2719 zcmZWrdu&@*8NcVgeC_yg?KqB;)~S;|GA|FeNm|;ZX_`I=ZPdp|s;V6$a&m4`r+)4D zW^JtNrmUSbq;(BCAcm?iNBwJ5Fb(tmW3=5`Ar%G^LI@8D5FjxITQ&Y*Vo30P*Ei_~ zmhN}Y`Ofcqf9JdYRfYVpLi~B3&m+L^ddKtUu3vu5pOxGDli8e^G?E8c{^@&3X1NrTPiSvH-Ui<@aPWAx;6 znZoDe-YL8 zPcfsokTHww45XaB%%@(uoa zI2sJh8Rk?flbK2}GhH-`H>+26+#=*vp6vfT&=>m$$ZOR%`je!q6iQ~&(=5dnlUc(| z(Uh4=mb7FcUo;I?EP2vda3?umFy<0GwlhW&d}!wnLf-|eHP1pJlTXp91(spo=gO{6 zk^6jj*Jp^xHx5MAX8}@b=Re=Q`OGFuu1Hi~C1u5wD~hF1WmUu)offESS4v&n4|P(Z zPK~CkqEVMc=G{2JFW0<%o zk~=&(7~^-sO*}W)9QiGQ9&QD`DC(jtd5J7~h@YQ7xb!i^f%!FUeGc zXzZ10_m`WG4fQwN0qg88lD+)$&g|EDwIX)F3YA;fyk+7iK~*$Rj|gZ!V+9$4>W~J z8|*TRWtSzfBbEf}y_O47C6!$!p}utofJgmx!@%=cE*iK7tpOOAEw+>X70GrhNR>=O zS+lf?mxi!?EK>2|%?50vgI#jKcFbYBreA=Xx&hL(ZW6oDF=_fM0UD{FtcdlVq|J84 z^~DEJaSv2{8%ReT=@!d_^+tz4Hb`3?{)Rfg7gbR7G*oV|8Y+$GpWWt4*>lN5H$r3B zE4wdZ{NBjGZfuEC+mnY5A2>DkRQv=>&!uyzOuV>|&B7I~Pn(k5IT-Ie{tiW~+}qpR z$$rZJGt#;A1~NQ73kA641|2ooOUQ_=X`3)i(;i~=3sqOfGJr`Ch7DOttU(XLY%yrO zK3#eZnl>MEZePL?gKmN;YX2UNl(wMY61x0JNoL>#d2CI{Hqx>HHi^70x+%W9Kl& z4Tl+nnQfc<-)t}(3ZG7d*_-gLDFv3!nfC3fxtLMNq-G4Z0w?Sq9!tfAX{O`$7r51m z{O$ccO{bADf#hW*6(9*OkB%nBegeyyZzf};a#Kz{Yl@ktsl}RqJ-jK?xM;E(LIX}6 zr<@$DeJVzGH*!829d8n0H0%c?j)pjqC5^}gM_!2dC66Q%KZ%N7*lCi7Xkv)yQj~;5 zmH5R7iNZHV1B7;2{HWByzqGrtDjj%*kl%2kH}cyDgX*6^Gy60D^}#`Ml!p#IN$ys2 zhi=NAt6=sY@CdE^cZnvx{m5tazaun(NUWy52~*o2@rCN|M+M(d7uC7 zXn*Tp5%MmQ7m(aS@}o#1l|5m9 ziK{;%@gw;Q65FwV@#W*~y}hv41jwSQ=~~1e3~7;YDA; m2(`BD Optional[datetime]: """Parse the last update timestamp from artist summary""" try: diff --git a/ui/pages/__pycache__/dashboard.cpython-310.pyc b/ui/pages/__pycache__/dashboard.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ac5971b6b2625132a051e0661e30494991ffc038 GIT binary patch literal 46243 zcmeIbdwd+neJ426(=*dE7z_qaf)9~RiV_KlPrWQbPm&TP*%CyFlw`y9;)du3F(3v2 zs|TXc87SJN9Lw1#a_l6wV=D&p*o*Fx&E~b6WOMtlFWJ4^-6nVU+0X6uXYV%W>?Pr6 zliggrc_FrR-|w%gdmb1_%HE#;x|z?^bXQkbS6BV&_pVZR#Yy??sDUY;)32c`$otixKY)CZ>r>qFB+_2KE^`pEQ1eRLX6$FPM%Kn!IDv$B?hMWPff}F}V>sosH4#^qxhLAHPId@9Vus4F75y`m=_l$aDI3M#zQOn&p-{Fnp zd|Yb12j@GzT{z$6XL0>QIN$Bd3$lbSI+Ol`99CZx$CD&SmxEw8TN(a1l$tm@_=g=fz!*Do$3e0>ZA6(|!+taFTtpUcJQJ;ip%F+U(QS#_W7;>C$6MmugG?EIYmZlQk#7 zui?N?O{cxowB@CzGrsL-{T#meYxcAwX9XGFB957#!_f9IA7EC}dDpDzesAxmQ*&uA zga3+G@QU~!^!mIK{)aU9P7iCKogVQ7RHsJ)KqKDhYw77R zZ_L|)k^o3=C;rF1UEXf|@AUR~H{pNRr&D%ndN%;djYgjK8&!72^DFF}7njv#Uxlaj zzuS;;a_5$nyUOe66ZhPr|FT*i(`y@pHYSC$*Kxiy#d-PnuHnT@-OJ;q}T zzYKoC3rHHNYpIR&bnikSbGlSG_WUr<0pKIDk^B%7|9m)|i=z^1Agw)CZ7xd1iL8<^$Zy zfo{DySNm~yG*CC7Fyjj>M5D@|!-SYeT@7EoR9&3$s%xkzm|tF9^k#y_@=7$YqLybd zX?zc0xr(YQ!RjnC=2jO?PHC=MTTHI^)vGVhT$Qd5qJr*p^k-y0wLE({>eCrBi#Wkd zvIBo{P8~w))L|wQOm0W=Hh_8ttp`ZY%)Fi299^v)UjbML$GvJWe{s30ykjeC>Q)qW z@e9V06w^8RI_6)WmDAx(z2M?l+=a93-}u90j_Vox0um}2E#UC;skg1E3lnK|1etHA z)m`{gA4-yYafG))j~&C&H(5n79q#P;V3z85Xo;oq6JYusG$H(-UbhzT=+%^(#8G;~ zx}I83H`Bow&N3UeXI)RHQ!k>#`P8Q^GuxbI`q9+2^g2NAb!#StlGzQrk!z-}rICUd zT(t!I(jr!J;bOXr)}jS^a&Yv$xVE?@b0Gxv$idVbwmFQ8Qan{FCy zv{MUZukSAU~aQ`-IfwV%xgN$0^j_~$G6n@kGpC_Pq7C42Jox2`pZr=hS&Z> zIW1-p=30uK3PVLknNY6BSQ zdRke{^bc59tn(UbK^}H!%rUd%`(7|(ra5Nn+g4;9I}zpdWi^S)G@uKN7av#zvLuCi z=m9>958jmukMx9ewYqfd0ZjGkV(<_S;u{5{bu^z%=M0eMzlwv`Qwu4TWv?}WE_n7$uB; zSiwDjYs?0~E60dsa)2F-jQt9j68$eDk_zPAS8^KhR zZPXfz{$~04rOQjpSC`yVLQ^h;gXb^TR#tq^Jq*rZS^3_?+0Fc8N-e8NcOoyyB+7&N zsWuu)P+&Az3ues7m|LPTp;vyK9TTdk@Yu_LJ_)Zp!fu52dD%(E+j^_$C3_ zPvAE|xT!Fa~RpwR!_5KV>=&UHG zbJR4e{xFk|FnJ0|TZ6!{%z`UXPxBgS=0rxF-lAS{d$8ypZ@*O~@ovx(Imi6kMzbZQ;>7g`^es9m7 zvUN~HY%*@`9O5xNc9!rLe@KUkjf4pxa~Bqxz(*T^Li}6TErVGyYY^4eK~=-ihV7*r zS>PAJR9}Y3*R+6PfM-Zwfnl=i)+?5m1CEg+!7=&p?3sCg_Oe@>bE``+dJ%lw;zrF3 zB1@_nQ*c2?FIK^jxtP%qG9VjljtNR}XIUFd>fv$KX*52{8>F;pO}b!VavIMJYrJnz z%f+=B)GMr)z)6%{QQ!*IW2gY5C1dmmj$32cnqcZdT=*{hf-(}z8p@QdJ-`yc5$|j> z!*u($ke-fF7(`{sD10BeqMvDilzHf5&;qc}3+V;$I+QftAlA8!m zIhRxF9JwAm-=^epErSv6$CdJzZExVM?3_SE(_YVC0_V|8ZR7zddDPk0>{Hcdal`Q{ zG?%x^9Y)adGI(ssV5s zfUV-}yFtbPvOspR29!Broef8(mK%`a$Pcw{o%($glh5zZ$>;YTJ{#@_12wz6G*|P0 zd(6}HjfeUCN%v4>FM&jEW~Ppx3X6x1xWOza^rX9)Id;d~W_kj1V1Bu=9OW+gOF+EB zwS+}5Y!_GSoMDh|AbtmpYJDYibRT$bXdQ7;=&-yUo^fHqx)2VXTBMF(4b;MIEK{WP zCn_?xMB;(C=2zA1e2i$-ZFrZc0)C-hU4%LVk{BlACAB2k0lH$bs%nk38EJ4dZ04|; zRHo5YupLWYG}3udze+2Y@50AYVAFVEN9PR}YoIFCmHD;EURbUzwGolQHmpF@9}UDp zr*23P71_@-(i2hteC^UaPj#EeL|Qd1PF@r=tFw-1XXp7$ThXkpsWdxZU4meU=Xie9 zFK-`{yr`_-w{~_JB4jYoI$qB-Ag(ksBvwG3cGKRlMRSqb$TsbT9FR-4nGFt` zJ2X1WCS=c9dj^OiCuFbSQT$6&%_B%L-^_K8J!d`l3Isr)4mlFkQ3zfAk};W*F>~kC zay`ZcXK(P8F!A%8V$cVMJ2i!q4j~#b_duDWv4xNFY7WWNL|-&!0Bat~G^jOd-i)3U zg&I|3GplMba+H5%74R5D8R{M@s#**D87PM^*{Y4zAlfB2Vy4c3--o7S=_Ow^W~72B zkJASAESO#7-ps_ESy>LCNYQg-o(&T6nQxmYe~Y=_kAw{XY%c|VRMf4aqNyj~y^P}j z48Pz260${5Yyfm}RypGcZ~=_kN$%kf07}vy9dOi#%J~zfN676@G$0Up)@y*Jh4f0{ zl8F0M5P%ESQywkVXns1jIGgg>$M6 zNV2Z%f!4erBH9D77*CBk;4#-EfzA`Ia8F(o4f#gi17TfoK-ionNpPWQBP}*b>iU{_ zKwgmyS2M4^)XXR!f_~<5Mt!MKXy&h_y^`1eDQjnH-9bHNQ_nyXwP`jDq_w!w2O?~k z0rdvTL1dKY4ZfApteiIlku>iOZ^<8#{85uXmXt@nydjBeQRYnE+tC0Cr$STa%&ulJ zZiBFjrS;+~MGyCRawKzRcX*_8&S=EeF=@aQ(wSU^+hH>!sIJV zI<<7_N0IkU=6@B*mYT?LoX0Pqo^PHVHAKxD)h8RN>nUYHA0bBos1&#~DM``F9Sy8vZA7#5Gj`1QfCaKt@D;vYO1_8oC)+<7PJ@~b6%1s##gg)78NO& zn*v4opOL4DaGgleZ4ww;@Ne-8oNJz;8fGeisw)of3E!>)oPV z&K$NXnTjSO^2gUt!1cQ3msw|T1r4W zvcUf%z>e110ge9~;7Ndsh}AP1gMQ4Yxj_}O!AV@VJFjaV1^HQs-!MhtPN=-4hWuMN z2Q|ooCjIPsE_?%uzntfI1&EIn1i`C#MX&D-OT7rOuJj=&313WIIfc6VNlU!4ZhU#?0kv#22XJrydhyEZ5dFU1?DNKu`}JmVp{%}+=k34|Y2i4o z?1Uh?%e-%DV*ny8N?o_q*TCiR%`!_R+_76)xTmu`TPkDl$42}cLCKp;U+onNwXa#} zpim4K?-vSv;4RQ!j07Oyvh~@t8fn5N#?jJexa($p z-6&R2X6;_nno(Rmh}NVra_`X^;W4FMLN9L#?-0}lrEM&(QN$6{AsTm?=Tx>g=4(*G zi@H`zcn8nW`sFS`&a8^1%g|JPueKE)Qa=tL5BE?`#=)tQt0_DC_wbS1$>D3G=7qg${+c9&)v+ zD6aLWtHnq@>Abf8q0Ug_w8_vhEt|YS@j)%{1@__vOwm}EZMuju?x88Q$f9)?9Xl&R zcU(G_Zy`9lhmGK@R#^H9OH=q3eB4pKz3y-TH;N*_+#QZk-6W@fR?rT z^pxq!V6|R{F+^%Uv{{Ua4}|c8ZV>Cj32bHo3k(Ft%$c8jW|HwkZJA zYkWac!)QS3sSV^ndEUkHQ8~E-8pJ^1fPy)QV5S1nIFyF!_#N9O0o((!NpeZ43TkAi zF)6O&*HgqF%IuPo)~n+g`&AOn5(W8p9D8qi9OO5L8WJVwdlS<3fjHYiNegIhBHK?> z1^R~Y>{73^tRYA3LzEtof(NFY!;H%K*M0*0(_siP zkFD;8+?Bl!sp_(&PNUqzNnOSEx-LQ1`Y~zrOp$`c7|$GY>1BNCPz3B?!>?`KtNc>5?7iUD0E z8Z}@NsMyqUb#`8Jls`*I%r@p%>lat42zOmU{->m_L*Y$_q{69Z+zU@U|MY2M5*l6& z!aAGW@`G<^#vC*O7&1m3&p-Fnq#FTY>stK!N@LAMW>+;6xzJf!KB~18@Cbol)BN(H zM?a-{EueWX?Dzb+DvXv#WqO5$i;LBz%f}X0E`0TQJ(9PZ}|OHPhBtaHY`& zbZuYfeZ9E%U&IBU=;U>XRpBj-oR^iUmP=4Y9@7>~ zHf$JaD03`&f~fmYPOENAxn_*C$jlO-uy$TX zxWTil`%skdtT|8UV%pd}1AhXn-vFq#i@+q!y$XlNH9*JY8`Jl=ZfrL*8ACT2B?IC` z4dQPy=d(<0oYZ92>erw!`KNeI-dAL7vuGuc)i{rkB31w!<# zI3PsFs!CdLDRJW8uDk;kW{XJP(^J}v-(9q@y;zqHn;|({!t)vYzB!E~JfL9)N-kL5 zpaw62puh=+s;&hnyVjusZ^EbKdg`TAa1+QRh@@wOEV;TIQdks4fp;WUQS$z)skd|C z{uaT|VgYex)zA#lunKZHOWzB@xi{`WlPy-u^ay! z?VgJ451)JN@hJ1$Yo zXW+^QyPNu#yh>$1b^pi%MT#7l3xEs2;y%Gzb9#z!Iz(Abce7V(USmGW&HFHU!BhC+ z>f9WNNf%GbnfNshBE>`uqE!UH38WA1a|{ZbWGIjKoECzOdKxFT$L$+AoAwvzxnZaERd6AeMqBt+R|*%3vlIP#0v|?)s&62Pa%2j{FPEMfv@aEuco=`g zUyE{Zn86bjQE_Cm8o|X;-niek#J?EfKEjFx!~S2qr)cmV>Q&V8X$;(pllVbg{Z|C4 z12_QQ1#WP_OV|)>a_>~K;30%-5L+d^Gqw{md-o6=iPNdIH?4kQHE$47QF3!x< zm%Y_R9+zfjII+x?{LGBEJUcU^h@^zIiZbvhiL!I3yrDjh+{nHN*LU>^7Wf#G=b607 zgmgpQ&t#SfJxml0u!_bot#GAKr^q@fQX562sL2~UA?i2Y!?QkAsWWYp?{1c(jY6@H z#_lyj4FXK~88!`b5uRL0~kwyEQj!Pz7D1*eh3 zUi8nWe0b4&=?7Bqq=)ef|2h0;9)Neybl!(gy_dytZo1$X{mjl3&htEn+qV<-p9e(M zX8rSGjnt<90x%I=_@P^mitvnX%m}9|I-00IHYY+9ulrNdl@opH578d=M@;@b6H=RK zUMZ~$q#1P1pYRf0+|{2m`M;USQ0(SW3vbDhblrc%g+C#jm2gl@iM|MLZU~xHz)~D-gS)L4DFt)un*Q+4rVE4Z3=36St{9X7U$I{)!2$HM)ns&7)qO z^Ov}=$<85HT};7s5?xbCE9P(CH6O;+ZMr6duF0coY;-{uYca46!((@3r;Bn-sBy3s zqlEkdj)75O1h2$|-2tYFFG%)ytdRth-Aj5O!$MV551dZkZ}Y#f#J5%PI= zzjshpwSZfcRV|pgSbH)+Z({scXjH)epmz(6%viND*e& zC5yE#dPU2KSC%o+Udi!3pvED_Wx!!)9?d(zO0q0P2IQ;){-Xjf{UQ7h^SM0V81Y~z zNvBMiQRI!9GGnI9D9XTW!XH(KVikxdF8Ip?9m4}AYJE!ReW2+Av$0&_`V zz4qJagIeVf2-^;I3wl8a4?LMP@-@q6f~XveA#juj*hht6ZE02?mZ7qs&j_4DhZRw78CRh{GpiclL3#vBaYZ)KuIy92Bc0Fnk8Gb_SCQ?)8LP(!?F zBtF+Q=_aV}<0bN~q!!VrR^=wQ>Y_ALqtVe&um-14$}}8UQwoeGI245y8L78z&6rQ1uy$-4h(|tN6xG5zq-oJGtiUtzXz6uS!7;v( zgR2%eC@6=&9ZvLab&q>H7;#QBPC0pj3J&lBsTt@sk;0Jxz7}b$2vj8^#eO#5$bd;k z8>MNrmasSjF4D({U@^(vY%RM9Vnq7ATH1Br7Q~RWloU+2lxQC8@KW9Rg za5jr1{_vOz1L^sX;((yC08NUzjexy?^PK>1tp-TWAxS33Fd`(!pbSP^h?(8AWpn^^ zEx;=%I47rcgZAQS3X8i_>d$zTv@aTqXE5LrZhVPbC;Rv9ticIMHOYP5JssoB&f3nf zhpF3#^CnOj`MR;R{a4}bz5CkW-Of5XJdQ|#Ime_2??AKI8)}|wo(RE^(dWabpsT{! z0%Y<9n9Sj}6`$wi9Iy?EJt+ZW%x*a4rJs7u@vup*E3_RUaFP}vxi>;28lbtrG(xG@CzerW9y^s`yW_t4`) zqQ~(o*`=)hr#z24ht&_s`wV+oJpT(}McO~SFoJjCn2&A@f-A^zbt3cz7;&lh2Ti^C zR=vE|s#%|b{^zIK=qXH((98(GpU~_XVflsLo4E&%YSM2S2O#SUD?Bl#k?)r=8d?>H zCHJXBu8?}$p2L|7wjhx+^mXvpwIu)^kg+;OAQN_1nA}00ia8eM-q){wj<`lO;M~PBW2dLCqhbOvyIw58cRpKJ?H{;HHMl$LcnGvSTO^@ zhS3w0`k<_&ajYUlV)zLG*tzO!$fl8W1j4ec@Oanx*86~e=lgYlKhb9b{!if~1S?24 zJGk7*YHC^LA-YGZs02XZmk0o{*KPrT01!hBAn-1F1?&I-M2+&u3mNa;x1=;iJ3})Nzg((G<@-c2-2J%Eg&JzvFt0;xf zEyu&>gOBzC8c>&3p&X8~SRJnd>I>Jl%q2K2!FLwpNOqX(Cb?pg{4N}|Ie+l0!0CW` zIi%o`p{qaCn|N%2#E!A$tVzc95j-{gp#~NSHb5MNY#brcg_pk#jXk^s7O?1yEYM<( z%H54Tto$~t{4SPHWp~5xZ3)U;jtCb{fp9qRCM&!p3uwdLa-YK$0UID98-3TY&{kBw zSl+$RukY`?%V_huzQ2o4u6+|cI!i@X$>2UBkq`BGSjB{{Yvm{Ykg}E2%&z(O%DNLkVCe)2Z zr6*3EpLy=F^Upm0+?mI;2P)SH0N7?BZ~`2ZG)0}oXhXfciUlt(t00IRSezT22+%?c zDu9R+;2Ja&#X`euh)%G#-WN1MOMPaxx`|G?+-11^Wm|52p`ZSxZ=mLyc_(2y^IL z5Yyf*zMIQcHgh>My>8updYR@)(>r7!k)iPT41Pfl4dP0O*C{+$+PsZ(c;D#d=um~= ziZ*M!E_p9OSB?_4++qB1sMbYZUh*8?`8t^6*!!kozYZe^*rR8phAdd0s^GwwRgEd>5tIa}i5@km? zAdcvhE=$u^9rm@|6CN;EPrU>`@ajdZtT*?$M(l=bd0fMJU85V`Bp#XC%@UCiVy4>M zF%|AImBu&d3u2|0bwRKQ0DD78BVk~ISqyE*hhsgApJ;6vHQ{xr!V;M`8iCfpuP&{w zwAUiAYb0Sc0XWgXECRaoz+$z2(W^f6n*io-;s;JAe7H9-O2d*jN;tfKM(;eunu&3;ws0eQ)GXzjEx5h(v z+&qeiZ6?|w*W#*M@b>qJ84lwGD{zh}f{fbfUDim3EUNw%Vc@D{%3?z$%Nid2Yjt@C z8X!pUcLqOMlTO>Ikk?y97D{899tH~q0&AwJg=q>mJmVu{VbPPs0YOeyCajcw5Xb|J zs`*XW;odU@EwC_c3J1Qw8CS(&yYUBT$rQ}Z$v~<__Ew$A5|d>lvD!%bI;TOs8^Jjtij38gGWHeZoPRHSlhl?jJK-dlY$=?m!< zQLqw~C@?swLJas$iX@S1I$)-xp>mItckE(jPu_80sw_C;Wgw3cef)B^&E-WW_fYs~ivt z06aJyAmBR3fCCm?CFn=2XkLgg&BD1yRJ<8gw;6A5fKs>8R;AIAy3z&dQ8XaFtD|0uwAvOsSWUfPkFh zv5+(NdQ@RVnpNmUmmoTWJa|zFIR+==EMKKw0p2s^CI9M-{}Szqi_3@|2HQuY{d@?v z#~El(Ym6MGwWLu+Y}j#a;3%T(?BX&IKV>mJH?Occ)ZYoRi?tm$Ggp+8)JuYpk_ zElRyE#KMX2ZwV#Ga6tcVie}W}=^vFG2ndrNvEa)MR0x3$?hzGx1nX5n&O)2JURN7Q1Af#uL={5K2DHgg0!9K+jXYdO!y@cF(Vm#%%Q;6xe1iS&asT7P4aFrFEi6B9& zjiyvfc^XCtU|alThGsVqYJ`2k)lS#g6S7Ke1Ik+lAGxMk2`b4gug{c&GZo4qvE+=VWDpUAxflInQwW*_lpS^>RYvf`mvfB_fgH;Z|*B6_4mj1qlLsGT58b8VwyGi zkmNCL**3f~Fm>Z)bV#`C;dFdidK9>-YbIF@@Z@%MRe(&E}NS=07RlVA3FzMdA^73|NI$Cts34S|^u6I-( zH*9i#8Qxv**StIB(^((iA>7eXY3mW{*U?!w29T&4k|=vh)v)yFdvUxuFv}>Rc=9N5HO@EI7X9<{KC}m%cChyPjWgQu zEqJ=B5E2w&I#>=NQ0W!I`3c6H!OUhLnjz*Y3_%TsFjZr6N#?q_lzUNoINGPd)uRn& z{+p`>7`9Ag*CGzjHxM&zcE0!QjQXU9p({mmquipzfbC`uzZcUi&xuMLPUZ*dGpG0$ zPDz-d3^fA9%sA3qh9Ne1j<0y%v`uHcj-ZJxHbnN1OlMXPu$OpGW)fM73rgsVU?B>W zOvKp!&_Yic^+mK$I*h?()t7M=oAg?EsFN^7W}OwoR3IG13BDe&KM50v(7WMS41;kj z5M8iCP65ei@m#l>)+;PK-AG>t`a!ET;<2x#5a2_QRIAmw7G{h_$XEmK&CCMF(@Z{l z8K%k4;5`KMB7!JZgZSs5l?omsnU9UYSt0t}oY{}#zPtHCf-LbmF#3-tuhUf(I_hg^ z`PnJhmwB+&8|z8rYR4kJq9Qb?>rD8Z?TPBAaFx9ya=nvs(_^`jiIJ>g{wywae8>>; z6FiC}A**FD#F-9;82&#DOaYQQ;1%HCQ}pv5e1||(H34lA%}OFm^UYTSQqQTV1O|uR zed0#h6B5zlR6r}IA(9&T6KP{SwS3tc!*&k(zhrN)b7IhwjO{q*`{VH;;e)gsbIz^C zw&))_GXAi63H@v03K|8`SWlM?*a~-o8K#eQkg&9Axz3Zj2m})&Q#a`HQzawPL2C@>+8WLGqha59600Xd ztA+pRw6=|~ejhsGMhRVVgUD1ba$2z3B2F8@U^(h8_Gc!5b5SGO`#tzKqZ+Fs{_ z4ctlJ`_Mh_$#k_>xk6F{>)v;5>4Xl(@Z>%B&E9oS=i6@IPOlbFHhJG#@0L$zt!@2( z^L_H^yxw{$B7uI6u=9B)gg6D42Dt7sp(G)!mLhT{qo%2Wa9oNDr-a*5U*ge^ut+5k z>VZhz2uE5)pm_BaUVDqz`ph*EgND>`rF0W~Y8Xe~7A){8nCKAAHe+CC-|u!o@~ zdieuxV-Wsc4A>(i7!RR7>YtoGvR=6iglMQ^!LwMxbisa2R!kT&$tl8oBWEMp3JOM` z2cd~MvzErL+xe(n0^@N)fqoSy2-33)SQAW7Ooza%!9mqvUMyVXQZLVjZ4)*b=87JN z<%Ra(UY6GqPDY*5=ZI4QSqUK^ZFN5vDSEI%GKv)BIjo9af}NBL7fpc1)B>NPDNbr3 z$2C*IbGWAjozQb$KO{n-BB(jHk@p7J2EpHzsTMvlvpy;qRN{MKv4wfmM>tGYJy1W* z@&6enH{<0M8I~}^7!rS%d1RqBi$qD7$;|y(vxOE|zv zoA);v8G3(%Wr25h9^9V;?yrDf(Rb!xP{Ym!_H@DP_w(@LrV_=TE{YPR52Si98jbz( zpO-jEO3O%Fy|d9?;p!y@bGvsoRSIH~1L>E}dOOpm7!kgB7rWzbRM4hdXSaQcoxqvH zkqP(qY)Nm1j{$v>>Glms94&^A-d%>x(|(q4G%#!)89o3xqT^D9Nkb-m$R!5%0@jcj zC4~hBNwag9PAEqg1zj#?9sJ6hF&3D#$7C^vF=Qh%Zj z=|fF2@fvA{twYNY=TzJoI-i(BIbpgQW${&~58S{;DPiOW;`$Tj56%q;jWGOUh6zUL zK~EyH%AJVM%JxECG6Y|@_y~2%Td*y8v}Fu!p(8|RTb!gVwzs2=t<WnY5{_~`gd4ZptV~ymyzq@O@dFv+@VUbcEP`~8J}EKkHD1DY1GMeSx*#`> zvV$BQl)e?(S9_sx?*f=BlTb<%!Z?RFcWJQ@hL<|0qZgO4Cr^FSy&L@J=ICi3dEJe? zagfcUF0A_JXk*7Xw6Tcq*ZbcACi z)VNR5>mJIlNs-2GxBv_+!FTR)>rhV42cdJPIoiyh)JI|d4)-b+0R>_1q#T5$v#SX5 z6fe8|I>4&>bD)TwumuNx+34y0AZt5yZw&HAMcYw12Rjzf+H~XFNgBH7{~KwI4}|05 zQCX4>?e~cj-N4%FYnDy6x!N8Mw^i+TyyW}5Bgs99HK^ zV1~957<1q0YEQGzd%|f92gV%LQ^(SJWtez+P5;)*%CwB%1MQidoSk1?y4(YzJ1Xtf zgY8hdUF%v8p4ihKhcJi5^V?#VMTh(>dK-r6od_II*3!*tq@w5cqcU} zBbiu|uQPRP%myoX=e8p88|fn4Y%P8PsbVC_XuGRR{1#>ZTNLbGMa!X-aj2AWGK?a^ z1(#YugKbkNil=jSmGVpA!$3IGEwfx`f$h>a74t*(H$5rBw1OGk3AIiPBHMf-r_ls2%rB5ZwXz` zePI0Cbv?AR$GV;jjdU>Ai49&DdunE|mH`1vve5tJwEhP*WKmMDPoRa~c}t4{qMvCv z^t*|L0rNBw4&=DwY4mCrQG`#4^*^Ya{_K)ZeUGuiLkUox3j59j3?VPe=zAXSPqzFCoyCqcC3zJqt!hl@Vp#{ zFLu0QpRQ`*EwKR7q4J2;K5*I| zA|TKO3x*Tu7@!$Igr*56JOULOBlo!EIfUg%Xb%0Bmk_%RR{iBUmrFTr{34!kV;YH8 zsQn|f;s))3(y8ieIEAY8H}MzsnOQ5C>9Z(ebg1x)GE6Fb^H+#eNa41Gql|XK6`#v? zp{niLd+}Lz<`8PM5V<-2<~EB3XluFx0U5o~v2E+p4oanNpGT^JwDDB5XJS<)9OzgK zzl--%|BlJF%Cuj@)h`m=(7r-1CC5&E_ugB^DvDQTk=gN~hXOyrhmj--HTcKC7b%0E z@kuhCN*GdjD#2~G`>KRku&1-4NMneUzNmDHF+Z@5mLvo5o@zkKI(plu`2wfd8e)z3 z!(;vkQh|%4!~ckea`8EWDgbu0#JH;d8`KkfE&LHHK8;(oPZ1xntxwT^!Lh^-x;A*S zjpD4AJt9GJnq$2R|$It(=wNSmNxnuW!PLZjq<|4LM2h0m2kIF(i!g|Z3ES%v39{= zxBYPRF!+SXw`F8vc@9se9vP z;XCd-f}(dHaqqqBhiRgD4;MC+BAutCylaIX#2RSnJ-hVf`@c?=n#Rcg&-+ zi`b;1qTKnM#6EN6_yRmL6D}VZ)9#X3q%G5>`xJ_Nr!z3%!@EaV3EMKa6wVm{7kr&eRwT+ocHri;&l4SUM!RvQrt{)?l7g!? zEeE@6}Je%K|r!M8nK)*E{xl8tQWjYu}KkvAgQ?DMdJ zH{#gH#@&cnbF+64=Oc)4b1VKwy+ht%{EvAP-fj5b;oa^X!T&gG_m28Iy<`3^?>L_1 zcqhD*c+zg~4u6k#Cw_PNyWl5tw|5V6Z}L8bviIVi`*39vzx#2u=sn;)h^u?Ohj8U# ze-9i}AMs8hXP?LF!}h8)*BEAR8T_XN)Nd(V3xMv0H$_Y{6lOADsFXW-O& zKzj3>_bi-RZ}y(^&ZE7E>SL#-Zvi=Z0b!-$s5$4rf4+x3=8G7pc6&_25*;=vBt`CKk#Rbe!9jfMf)I0K?w4MdQ_WayYK@Hq8NTXD0hR=j<+Yp+*$+T+?6dyCmO|6^?a) zON*it`Nkg}6Z;B9r)M~Z60B;QZ!=byAu{0uXAni68=G(ota=s7aI7drx06dXgBCA% zGfU*C#57S~7&qoG(N{{=p6uaKC(AAueul3^!ZBin`#YNsWS8`vUU?C0aY^Hw$Xk#( zZM_A#46ew=J3R6M-hpvv+FCA)^A;INo8Xze$9}$o+v7cW;pgJ_80z^R?-$u76Tb)b z5b=9(vYNMe;sd+|;?ppc0OSe@A;2J{X7xEX1DIXKYFsR1NZ#fkU*tfKx8a3fir=PU zVQF7G{yD}`>5PQaAs3NiB+TNbybLT~BCd+%j!t*6hzj?+24aU8td!n>r4eTuC#7+h zwxrU&M+vH^?PHBT#=;d_Y>5n|FqL48VVlLGZ;ju$RXrnvV#5*J+S5A~}1y>)|atl`Zk z-}A{}3SILkYW*n4!u6*Y!`ksW+i7lN3&0)S1^_;%i~D>(bCvD@(M4kOMQ_pnz^&`0 z>ii%LU>VvmbWGhz_kE23+c$B0%SQXy+#{duph8pxH+Ev$X_d>UW|8S$A};pYrvq*~ zwUB-^(Jsv97t8=AEjx#W5wmNW`yS}Zh1akE8T$BCFeTewMSD=m6U(X=LjI^OCf9W} z(NL0cYVj)c8PCkkaqlIl*>Ia~PnR27)Q^}~j3KPmoSJ*T$>hZ@X@)5Xw5lE|SbYn# z15gcF1Rm`$xd|uGeIcRX=7Q*J;od`b9dub<6)`CNDipu^gbG#&9|%_fau`bhIo;A$ ze7^WO{hjTpY-vcPy9H67kc#ac-F}6r`3|%X0z_VxT2ho4Z3!1Rg5z7q4{-{x{dBsU zH^2bsB?rLx$%lMFg1{-GlBGm&Cm?R1Ji5wQP<6>yP+sE82GI{M6@V}U6;gJhK?eL>S1Jb8zr1AjyK?R|*R&E`;R3i|8z6ayA*qE2d-Sz4csXsz(Gcqy)&njxz#Fz`E zyNI+T&RueAx2*Xiyhp^*ui(hUYEdJ&U90T-ulkFyCB$N)NTprePzi;<#laT=42wiWK~pd!aQ=1K=C=B%fpkj)SN36fqTDI)@1jTt$t45bpY0iwK+H#(LK zwu#nW@v)N=VzTf)xE1AP#})3hvLCx^7MnTL0}T$n;<0a7j)ZtFEdmb3LOr^9M05br z<+TU6*0iF@?UiFV22lnnw~!Z)|4`@nQ*{!SjjNul&{_?&&f&l25QyU8*YU1UeS#@~ zH!G7IlAbG5hS@b_Sr}M>lsgHmAd8?8emgJgK%pyrh!stusODntpxcT^M^QEg-QGRa0m@YA5|oA2|5Y6Pe+9|_VHj5c&MvkR$bAAkr>{Hf1^Aa? z8`q|Ta6bU!H*D^o4ko?SED8Yk;|%(aUD!m{D~_eOIP}U105N0IVVd9wi>&2{NDN9q z0pdY}zs`eX>F8fQ;W&pt3>8t*`tGA~kku%CVsq5kA+fV%ED6I%6KRU#C(?0%`-jff zmTJK~B)OypbpWHcIRK{;i3;gKq=U5~8cHI^#dY%ja(o*$WdqF*b0^%7oIW0Gmg$qi z{W%$F1#{#W013sl0^E1cL%I(1g9gumqH(Xt04NxXvazJ^L%MLk>4xVeDp_QmsBaFR zT0tNmxRW!G3ykL!XSnMn|5^Rh5*W>Wo)M5j>*&$Ve$y2?C{mQu0d&q{$(F3H{W$>4 z#K2!~dJ)>9f)85^>4GoIvQ#FSQNP5-m$XfGFLS9P)DwweiNJ$gQV42fjwOVDx~4Vs z-@}r$1VkA`KvDa7cNZxrBkvezmWx(womt}rk_N^Z5rS}_H{NOIggPb@F-Jn-8#wvT zF`44kz6SHhsv@juN+tnaRkebN(;#Q2!O#W7+7H}Y-Dg@9Saiq^WW25eE<_IMLJn;0@0Yb;;7gvF#uF|FPad>MO}cMfd#APu0da~vf4l(!F( zgB1oeRD)LE(sO8%XBsTrF**diWORP&Khx+4x8)fA3n4f`rD?O_8aDX~Cr2BO7IUH6 zn?Y!7uI{GW_%$1Xa+dVmptBZY>u*DB{boc5p7UqHBD;dLUB| z4ZQ@`cCqI>>#Pa@Np%F${a&yfnsWrURxq6+R#dDNfkUdXkrNNLZ8)rMMLiSczaH>% z=!Mse1`^T02m{p6YxvZGHQP!U7;hn=q2y)?ED%*-EnyhusM4l+lsLcLU`@5cEW!E! z0kNB^iw{RGQwD`M*rzv$Cy$RAjR`cqg9Rg3n!x#w;sn}9Xdw~0=xP4z&_xoU74ilm zkhyZ*TF9V;ixN0unGGAEja+Vh#Ql9X5c*xiD2}l{(;Q>Hrcn)9h&upvjpU~``T+gd z25h5roe|G0!n@1_uDIzpiX7C;+93=0?Eg6=Q|ecco^7p-nVYLtPn@_v95oAD+JcH< zjf;uK{DW_VPJBJwc_vwqr$c3FAfi#;K#g#35>!c)sC=%Dtn`aK+Fu;UOI;W$Be8?2Gqm8oRxKkCM24Xom6 zDQJ`T>A+?HrdE9MBOGOnEdyUeu?88k$xu~T+q#h<-!G#mBdU)lGU46}f~LFOx~YAZ zh<#b{(ME_=btfJrNFISR8Fyn8WEj3)39i-n9YIpz-_o56O|?k;rP%F>HFQsmyYPS? zWgk(l5?3P&L0AqZctBX%-c+TdO9=QBIfR#`RFOpQqL~}pVQSMkwy#20#2zICOU49G zN&ry@hmsN$txbvWMU=rrUyW;rjb(`W*p=$<6{H5?xspT zQS&4WLvl+jC@8MHlUtHYpfy+s`eK}~s|pZ%30qis>UY_?9Za4;2??bXy;%%uq1@uw7?ECP*Uz&6%Z)>9RUWbAWY|Bi4Gm!+&NEsB8M&3 zC3?Q-X^I0laA99?g%$8HO&C`%nRJLdCVAX;M8^{bO@Y^5;VpRe#U&>C!rPw^owR!u z5vyX*z`yV6juCCtTx}7;26_qoW5Dz)pHh$D^}}0bBWM{G?B=%!rKGo}HzSn|t>fMo zI8~>3N8dS(B?*e5O@}h|f1=*0x6_Ki68AcHp;Lc@Nms8wj!OOod!2OJu}0DdtZ-jv zKPHe84{rym<4YuB`y5UZRLA&aLIosJ84N+9_hDWfny~YF)?BB*BJ@)Ta{)IygI*j` z@tAgdT+CPsp&5c3BQZkUFWt!CG+wUu%kaK6A;;KOnV_qD+i$%2EC1n_-_#wyk9Dnp zx!bNPQf%(sD%6_wYC?TKYh1!L?8oXWSP3Xc3Yn5AAoI1QP?EkEy~=~A;a{@bxe$th z5aGp&?+6Be19S>UcAV z1jV(*naf$o4OS_&a#PbTNi1WDaQ-lmH4u9^3SFVim{1%G+4>awC<0+ax?tpEM1(_h z=2x)w6VSCB3A!$Zr_ap$vzOi493bAz1GDFF(!qwj6L6unMH*m%0sJKq2vqb|9Z-hu zg0}?v3qSP*CSPRoBTT-+qzkOcZ2ms_SA7)+;k}&zZSbD#flRbWE&~l{)7M?{m;9Gk z)I;juV`!Df2b^Z&Ga-;qU4UoK8$A07Ceo~r@<{OAJdYkj68r8wf@AFzfK@~_Z$@v> z`F4`a**$}Y!Yi{UZsZc405 zKQQ?%Ccn+3t6%xbf5L{0;J|GEiMU5?+kbX3OQsR6ZGiQ@pQs}F@HoLBWKMxM#JgJi zDnR-KBnp3?+1zLFYXU_r@-NQo7p4#wD4Bn;jvjnx>b+fy)fqsTg_-nCLfW|1Jqdde z(pdsz{NZujk-?)lffKlFD8T85#s4H0uvT;G7=qy>Bn9ZM_w|WFc$;})248>!4fZ&U zr;~YeSRAO`gBndefwqU`Q+nCt^THQ-dMgNkZn-!Y}0L}siJ`7j!< zch`N2M_mB$C`#tgPeLzj%T2r6(~Fq1=;)}9(S8I^h(}xKE%3gKHbO&d;(4f#l zVh5j&8OT_Zs`V8&jSzw$o6d0f5PN-cbyUlDkq)tm~etZ zAJsX+krA}|!NO3Tfc>0E3=!HxJ%UaJYlsCN4nlKahg$VG!gF9@$6FTegtQccs|S$f4`6%3 zbh|N*$Q~is@m^2tK#K}gE609VO8o;OvY%nXxh^BInMS~JSoeqWn0=mk7NG zwq`uTNG;s7=m5BQ=Ni5=q*Vllq=V<`>9-qBnsri>-2i$^%pUsxyxyF1d^z|-PonQ)in)p7wvB>{V*8O`r`;3zuW;)j4m6GOMT>6D{5jYR+`%d@{z}NP z|E$Mg$1f{^|62QHZMPg~%ZiF*%D1?Nm}h_s7`cWtI@eN9^Ig>x-}D=N(=J&37V_@L zE2w8sqAdz}43h~S0DKxHP`Vx!R78x@EPvYeSJh?)l1)c;y1D*g1 z1Zam9EXK<;rx6Lr!xM0zaZMeTS9=j@Eb4uXuPN8MUh~^1|1d`iKaA9_wEb>)YbR&S z35aJUx`LR3tJ_)WQ6$^H+avAo#u#$u-MD*C$FzfVf@?DE7!yfPIlMDAyPwE~Zj2PQ zE}kf+YZ^#fZi^eFj^KzMj|m{h9g(!Td%WL4M?A`opwz_0g#=l2Mi>7qow3z9n3`8a zsFXORTPQGU)`*BhZu~l97LKpt@?NTxW-kN5!*2!Rj_?NVOIR5lOi5D>1y$30$F4#A zF+AZZwjI|}cBTiP%EySKaS}w6v{@tR)3{07B^_DNP`$L=K}X5KlCBCa{T&=|s02@H zLH;J3GoT_xO9>(j#r!zTMxchRT?VN54IB@&6q`&M5g%$51hH!5;AX@f+rjaQdOioY z6Rp5!xdNt!?8OEPy`k1HONn+YYRF?hDRwOA*;t zBp>cS<#~`zuez~4!>wUW7u}z!T9~8Fo1=PGj78x{hr^=@<15Qxrszt<5fd9sR@VPU zxkmM}f2tAXjP+)jL&5pi3ed)td!Tx;pBybf@m}m*>r_6@+V8=2`qd#40AsO54g;UF z#odrX681cP z3HD=GkSR8<&KKs~<1(CZyc4^%?(fhou>A>!ejO*&U?K88EmDVhM0NdjXih#k3WpKx z9|wR}H^J#c>#VRx4saaa7CCwLz?E3d??`{>`nV!jh{63veOlYv- z{-odFr5^_wXy14x5Nnl0&sQVtlyM|ea9ij!D@9p?NYHEF4hFsV5vW4$7JU}0P*<6B zA)ku`K=v@{Z3#>r{eK8Z+JwaQwm_pdb@#g2yT**=K2!{^4I-t9Tf0{`5mxnLg8h8{ zK_;@!iJo(c-bjj$6N-j;MKh)j(@)!$qVYyii>#>BR8+AkDo(VX<_u3My(&suit>k! z=qo#KkX_X~CX$~~yptHj%I=5o?j6P?`so-kf$?eLH4eT8mvWK!|XHT}Yr;?SrCLV|e z&aFM$xaZij21f*K=4Jw}_9DZ$q!c+KMJ%qU*OuUmiqQvDiAg^b3B4qNzqt+}D#dl5 zSysL{1k3KuYM5oInhMS#c{H^rqo)CVIU&~wCUvkm7 zb7#&rXU?2CXYM_HR(1a!YQrD&`d9`0KAd@1Q)enUVfarad7Ztl^b!NxqMB7`bj03H zHk%xAw<|WMIpP7D9eRN2js$=i2NgGy&6%|dYhpNsl@+Iieyuu~Opb+?nJoe{{N2Qg zGUD}mzuuA5yiB2>DgIb?U{6Y#U;lu@k$hA*PEfW&K@|R2TIXkdn^W2LohfYbrC3#0 zfPER+aj9@uZ9VXWPb}Adnh*zfhwaoU%6!ukyA=cn9`wb03tzt9}{fhJ)h1VjB zP$+U0G_A|%(0M0Bd;G>I>juFtY*IKhUB>;!+Q|RCWUu08eVbWG zK44QU<>VS`vzQX80IYnmqOE0TN2_R~UUs`>5izotEYpdb{oQh-DV);L?yz~C^&F)h zQ1-fMGh383l_avZtdJ&w53scC0&OBN6Gf4oNJ?fKv*+ln!2N*YQq25NFnc|jeo3Rq zo^(EI_CVI`o;|($&t=sdtQj(79V2HA6NikJ^TwP3W6rVtQvU3oT~cn@pm8btc*0}T zixp3oO{pkWoXW^SxTrh{{=JfziLh8#saL)-JH9eTd0HUQJ{_a3)Q4UYPY{@G$E35G z+dwCJm{sSLl1{cir0Io11b}Y=_=uHBQKViRnCWT2c zG3@Y;m_V1>ul8hkwNV}pm7na2i9U_|2QO6~RYskJC_2d~8nrhris@HJaezYeFL`N? z%6QX&4qRTnOmUcOTcc2PX+T%~Ff)xK#-TeNnPZpMuh?Z^4=+tF(>P+Ej604-g&)=U zRgQQcP<5#e6^>F%@7ID738U3^7$0 zdLG8d;zkL{8BJTOfRLw(8U;e0g(ybh3jac(z&J|=lRHWbf$i~ciRmO`g+L(B^G)BtK4nJoc3l{TccFH?kMkdOSx`GG6sYa-VrQrLaQn z)QyJcCX-RA6L?-z7R(bz%vu&Prl+H|)kZrx=YngoqI?oNibP+o=xKJfw>ur;WLpb$ z+8lvSvB|c_DJvE^q^%t*fVU{V^WYs|@L|`G39f@L7K3jLTcK>t3egT8<>71LbWHSF zli*R5sf}0T>MwE#T@TfqG+XnP)j{1&vxryeDGPEYq*@%B5g4>WtAF*Y< z9hTk;Vq|m5kFwf5h!-cT=yiEpoY9Bf9ZuRAPVmT%D61Cmgky2RJ)43RCns9UyBoVc0z8v3>-R-htY#LQV#++iPp7k9ZI4j3nrVOl#!Few7p}P(S1t zlLDh>o(36&=W93|AP-ZmB)ZfgOV+_^4lZd_`t*zGSw|WkYQ4Yp0r$D|lEa$Iv5J(8 zi^f#RJhSJflUq-0?WyUTD4CWG8kY~Hncs^~lu}Bb^z_U=zOTErcR|1AjrcS1efy;i zo1~=8gYjG5N}up~KvAO4I;_4V6eng~oMi3J>q+aKdT!Fn!&Q&~nl0zkW(=gw=vm&E zBBjk3Osg0&=XECzn5P`k51GaHQqqr99;-UGP%6Di%GfxVQU^47h28VJi+gVFOE|xz zc3?@Zw0ONVX~W^Fp*-vPytxB;bI<2h4CGbx?UOcdk@6}6=km55u6aAtdeNLEiHmyo z^|tm;ld`J^%{3qa_)W?`ymBbF;BfU@MWyG9mJAdvIbXDPplGeMZriz{?NW;ME$f^s z3Qg)9P-(`T^D`<3W>lV^Q8zH7PTJIPepBLOWSxEg%1e6ScsWU7o^-6W$9^{dhTdK07p*(H zXdPE`c8~95;6y;0UD@Z8*40Zp?9y6?DiK4)>qgU$m(}X+nc2px3sNYYTqqo(?PT6%S#&wtjnbx zL@}YLyLBLY{=wB3`EqvedD{QDzq_rsyiea7=-&mClq;etA(oyg}z33 z%V6QT`mxq$J-xF-`_ASs<(^_2G}?!fMJZnV;%a~>0x4Nn6au&wdeMU3n_k@d{MO!@ ze(<7IgU0F$Y356682##M3sIOS0w#D#O1fm3`RvBt4SkjUo6gPJD9x;s>b6K*w@F*J zOBwZpDLdXwv3vz|iCJG=H7ShgK&DIO>Mwkuv9F=OQQElU+#;K_aHq60;FKB~C1;bA zw33aL0Jl}^+bE|5FNpWX-<$T4lOlZAO2;g^<^KQ%$PU#7>PbHKY;Nhh# ze0V9lYzjR5URPEK|6Z9{rh(4W^C#UXD*HJ?{{(_i!~2!HNX+nHWSp zlc0vKGs!r7#-v-RR-Tz)S*cLIs!&6li8q>r9Rgc!o{*{ZMzUqh^c~7*K1t&t@1N*S zneIvQ3)CPKg`P^D;83a?>!F2f0!BcT~08O;Vxncv}L7@-LGhe;rdGmd87sToI{$gPnyLOg~x zc?@j|q;+WPIvp=RYMgAX68zG}J`{wdAN^~{I zX%ige9+97in7u{vAG^EFt!|b|gSs6azdt zi)K?<1)O8eYH|II8>*M$DbJHD`2?(p&|G@x-XF?o86-ZCzC=ZDShs4P7>0C8=HhH^ z_jZcVIgX80d1ELEd<5ng;0_zGdhkWT@!=yl7fRMY~+Vr|umHEUMXREm?iHGJ9# z+w~n#<{+b^heSCyvh##EfEn?qgqY74QtMv8uCeNP@8WJ-Dy2j&t)t3iw5>_n>XKZY zQm$_>d*6jj@sbwMuf{8k$$`T+{9ykv;jCfO7?HJu>o-buwsY%uO0{;$-YJQ`!Hj*N z!DWrYm-RB%svU?Od8p`I=`Itk12pmuVHxmn8r^6_bT9KNaDEB7iSh zvFBy2ZdtMNWn=QP$;y`}tD$WlK0|;5t};G<{sh4jMhPbj+y6fncDLEU%7ST3w8yf; zS51On#qS{Dq$uE*O2&~w)uHCGQ8OmqYyC`m*%aI{ktDOBiSdChjb8)D3+^cXbHsd? z$gim#bv@+JT^|F2E2g#l2f;PJOFN2d?C7g72XVwj%JhH&;w?W!MxSL6ZuMQ-{n}2g z{C`xqP4Ja2gL(mLgpoZ;-L1oQ57ZD)Xz_7~1ZyCL9|^Pc5NJn#2*8)Uq77qoz#;OO zC&C@6ZFC99>*$IOysE|eYXdLZM~UR+I*&a(h+qh*u(?{Gq64WB`PYddzNvDc!I}qk z3_u|^RYvC}*>$207MPLH6DxN1`dv3Mb2_L!^3XcYC}Ll6W+V z85NBVE0TcnTSc~ky?DgV4_-hgRd@2tKl=SOte0w~{js*2o)G0Qr~~m%m)u zzg()_ENyk3TUOB^l{ZR_yQJn8X;-V1)HWFJzL;$0nPvJlPcGHl^kKkGpCb#*^Zer$*ItN zGYge*{QSYSrk}Du%(2F!Q-oEx(v;mZH^;))@)CB&%Kn8%4gCxWg1ExuCs1r!*xtEG zWQcu#?w2Z3VYRT^Z}8`KBF;VpegwZlfbKw1L)qJY!cynWAU|d+<~ec}t0!JrDrBnD zugseUpneSD-3Z-iE6$iNrqNVWe>wqUc^tVM%sc?oz%d^r@ zqj19TR`h_GJV3z)%4^7vn7HhIAxQ}Rdf9rS%K%I(q1l9)E2vN+utOC#^8^$i0*E?c zq=Ts|R|~TQc2nibn3*!+MAluoL%2a;u{VB`%5M|Z1$ZABR@S)p)>xG^pIKJiEi4e& zQ!8%LDv`08gko1NBa=7>9nHZ$KEPLR5l6pORUEU@TaeP}rBGlE89kx1K-L$j5bti3O?`|v6 ztV6;1TyXJrqi~lHTD<+5n#^Rd1Ic2x-LaXx%AR+uFg=MbmyJAB&CX7ElZDT>GPAQv zGZ}l+S=O%fWO@^JPk|1rN{&j4*?mq*dO|S`4+zz_!oWu#LU^jNMuHml&^r+vL~s{^ z?*oYX6Rv4k1CxNu%&~Fpc6O<0DtUv6uHWe+E>@1zOM@r0Bw6!AX#1w}iGgC|0-Lid z7pAp!mt}D)>d3vpLdWg6R2<@4jBh*c3}wib`^G9%CY{xv&InXvXDxz-2r%KN)3G%h z!8`TjsP{K|3n4~XnEjIu|$p;sW^f_2lcH7RsG_2#oJj0 z-AcBm`R^(mVQ(l+Y)8uq%?}WFF?+gYdnvbuiWVSCu9$2@dyC82=C!IL>w@?{MNq;P zw$9c31w~{t+kGjayIZx&e6$bE#Bq4j0yQmydjP-{?iOtG&Gu9FLi_F058?AV#K$$X zYGna8U@zaTYhfs?YH_>Ud8Gl{n$g`;;hpCryK6}u*Ea6oSqLtNPiQ9!yRON&84z4# z)t>3(2cZL=Vv@vVs7G1karUA&-}nnuu^YkT2uj#1y%ShUN0Nqsi+W19F_v_ssmPg7 zU1uyY)7L=mM+OwW3+JRRqb|5MKg4{#rSM+)H@?R-GE3&#XH9<+c={4+A)=PssH4Q= z^meq@ceu35(D8j`LIKxFNq-GkVJ!@4s(BWhCs_AgGs!cdH}4{X{g>GD90C;*oWxcy0(dzNhcE&Zhc5=`9+%x& z-(uV8Y@xqGlr$WD0b9R86eAvT9iDnuo5N+ddEJ!0h+}=M?9fVbiXA#6k|)_yhb9P1 zl%dxT?GTLIiFvMoo@`gK%b%u%*4^zD0u9JdX!X!RG`));yLNBOM6*m@bke<9#U(?w`OAa{C~x7l_=2@tjGus&@0J>|kd zW$63&#F9<89`q*wtk+-jk>wrNo$v$O^$ZM?V-gnbb21MqO$im&H znC0I$OSoSd+IF8gJ`VQ?#r35F?7+jBak#7>;RO_|?PCwuk!RUIA6~9I1Rb`2zz}Qy zxtV=)*KO>t1F4~J9@#?33AX9cO7dCg;YZ(8lCz;e_cCI=-pcbuAHTrtlgBOOmrVCW zIeC#)Kart%6E*2$txv3mwf)T#Ctz*e$8&_E%Fw;XZ&hh|ij_!0Sv^aYfsYaQ5`rrT zK1Tpy5wBXVVwx4Z@Dkg)R&9N`T^F~Egl5T~&KJg7P3=kVik zK5USyD)#44sZgd08K3=>lyE=#E3o`X3)ox@`Zsul=RCn@-k}_c4!TubkW!$D=16 z2zCu_3%0s8>EYP27FT22I#*+pHyp#; z>)~jU9R+1XF4nH6^(hz+$GYI!RSqz4nFe9Io?Un`pFDX_UxwgNg)DvlO_uXT(2idO zZuOCsxUQ`;(O3xTRjlIFIw4bF#rK*+&z!1KWu&5bJ>DtcOTRrXxX+_yIIqbp?eqk) zip>ESWE)RUBGt@yI-At8`%h<)h3w?%ZJNkl&FC){m@2fQ|2dLfgtB#uA?{qn1rfkhPH5(_ z>96IHUxw;lo1sZU5C13~mm5*6sq{H^^z7pq@~Q%5SZ{}{4?Zz#g4_=#`XbwLu9@_) z*UmkHn|LsjJRkbO;8CRjn;_;a-G?K{QFKLi_MF z*Y9<<*xjwsbLF>kl35PpzD)MU2dP?F$*)5(7dn)4(35=$wenFUVhYgVq1>I%p&ahh z7$;#fr(uT~K{{d2UMx3r&mlZNv?{||es%10&^l&#)16UA=}vEcWO&2P(_t5z6_Lu*B`Z=Z9uRMrg1HC z4Jg(EEIgtIdg0Wit6ASWjsVZ|PNL-vqg>$TJdt& zuftw+8rf1hp@qhYwq%P${y(D0aI2~K66H4MCLZKB1u4;C{sfq_u5=-5M3zy(I*9@5@wLW zq)&{3T^)Mo6CELoL!W%wAjIE-mbn!`4Sk1gzqBQ)Ki@UH7Up3OG30UV6&;)P<$Smi+5P2>;)veaD3V|O zqGpscOc&UNFSE&H_QjW78lE6bVf(HX!56FD*Cvzh(7?4n3*-Rn`KBlzdDB~f(igX8 zMYR-her2tE&xnDW3;dolk24 z;CMh)MI;>ggE52@kUyO)C1juQ|NY8$Zt!6h$qQaqlH@rbfRodoqs&JUOh7#x*jj?X ziJ$>M&3edz`2FTDu*(~quOhR^Q^DOTvVpvH^0bQFM#qsM>pFV;lzow|6-wzse zWHPxO*Ev_vXijd2^(Fy+p8)s@{(+q@HsUAnVgtW3=W;yoJ>By-U#-}{-+9O`l~^5# zQewHg6~3K%()g~;%PDXwpH)~^5$sGB`$!=*S_Zy-g2Srh!BfxJ2PGSha( zeDX&|@@o>PM`_y;*bwYMfafmXp+0q~xOA$A+OZ=ZhJ0$V8ao`&0qrVm<7^-jU~?y+ z)WEXwbCX{>U_u;Swpip6$5{b@1#EXZgZ)XwL`s5xNdi?1G(kOlVV_|{oa?x@T{FBP zR^&63Mejv@4FJa1Ty!dQ`lj#|WKE}yn)&DxBKkyX@SPNrPgVzYsibG>Y7}PY%pL{^ z85^(d9)&i=v9a5=H27gEDHIlKf~h7_u4zE5rr>rH`FaU&J9!6!MgTQdVZ;_u&N2;* zj^#Wp_|r5pMOdZ@zLG}zvb7)@slk5q7l;ghu)gx1J{^42ObRBh!YSdp!}~LhLZ_GC zMvN+JPJUu0SCk1@UDa6Q=q%ttY48&Z$t=e^ScUab#2zT$x1*$@Ji!EQkYZH76g;B3VN68Q>I|*#TMGQ1G=0Fw&|y`SAqus1V?1 zC|)(v0tAHsYM`DSIjj&Ntad@B=(*n2aae6{fiE^>YJMyr3(5eDHR&^;rY|dUpi;*g zxI!cT;3+OTgn8MKt1eF}1 zMe!h~?fAG4!JP>F2x@VZpBA^_jM^btc z$e%}u5I*8GuV2Au^C60g*Wx?|nYLk3W67tGlYY zs;jDdHY_&&^|&GWW3xG2hyV8WTvpZls%~%e2|c{czt{U9l20-8NeX{V_h6zk0x@Zs zZVo)yxk6_(tMR-%G(K?5uq;j*qvuEO$_&iud;|31Y%=$(N>c6!gE&6!O1NRZd7hb% zOG)ONUyCp~NG=;kJM z(fLv|Vx2BW$CBnpJHveGEj{z1Tb7z3Iiv@4PSgD8N1`i&e;c4dH%I?~t^ur_`OVa7 zu=1+3e5mAW(}pMc^+m-qu7OoG)>V0Y9@SJS*D~45e@`ocXkL_5{z$>1mpcO@BAs z=>)ziKguM8JRDe%v(pGq^MdZxaE2@0hrukac3+USj^-%@8xXt_g2lr6Q^#fm|DC~g z^5AFwX8vH{=gUTeQ#Jak>T9NtR70Hd412xX*IbO$6L*-{dYZL|;BExf==LG!y(t@D0KL5S%3#K+vDyTnjwFm)ti%I!6pvgUau_--;|2+@Ch3 z9gCl7f{<0PxJaL*5bHr8cweH{Wdgyr9mn%DKrjvX)%%A@=XHTWlixRFouYoyU)9%o z8q_GS%x1eBvdxQIRPRMWdDhf8q~?96mccTta@hTk4|4HZ2}(axG&^1Eqq27@Hp;*Y_UQZlW-v>280EHKL4rF@h_bPkr}Py zXONlvptKDFF8MtJ?r~9dQPKq@wI(tlp>S?-d~&$Ax}t z*sB_Rt{R!CoXr|x>PcjIME3ctPSU3k_7yl2`aa8f$mt5@<-AF>&sM8(4^g@QQqFsmV_lL2@H2px$DF zG=gq4u@kit2$BiX350y1)FMw{WM@X3&O8L97$Zs9KWKq(5xn@N8>;E>3w09EzIcDd z#;=d*PN(O+%wK=(s`QJFzq;6Ns;21~JbuZ;J;k0FSRPFi647B$O%At9uJu`sL5mRm zTmm!SwxqwQ1hbnrgRfo@ANXZSh(4EghFv4LPH=-@7Lr?I*g%>k4%l3t^~7VvGwFK* z5s>w)AsU0u)O(tG%MnzA+vBNY_tH0>$giI00mB0!%PU~AkT&9vm_YCm3QebFI@~L< z4FrSuYflzKb>Qrig%B%v7;o8;Wjtp^Zp;!AaVfzv0yED)p2_E~h?S;Efu~mVFu;Yt z{x#vyxj7#BHAfTEY6R6-%3O9gTgp%JVHg>+*S=`dmg1kRwI(mewfvnbFp3VhGiQ-k z_SM(f>RmIWK+L*QDNo3vXPb~p4Z*>Q@*E%Af?pW+`Vx!aQgf7k(n^W%RW6&w6jO_k zd)BAG6a3@#11&ETr_QaMYCQE`c|3*$?}*uG>9rE?x}jt$8FFM923_In;q5oN&CSKm zlJIOh0WIF~9xtX50-MC&-Qa@7y!*xr-Gq?0dflr;M(ec%0nuURrJMShHj=kx32$4o zDL*7-C6YBqqNL5Cv;|8ER?&RUzuMN*w3eEyx%v6trZw2KU*Ws=ZQ<_23H;*&8T{mr z(ScR(BtxS$fvqFTrwI(ShC;3N1QZvTiGajV%`Mr^ZJ#c?*+wEsqS4LNdWJ}1v_xog z)jC}cyU)YevoyAq#v+1aJ~W;hhg)8UH3dJoy%%ibm$qB7g%6}uXc?6ZRPVjb8J?8_ zcf2@F3OP+IF|}=cd9~Cyf>xHWTzsAQyg~3KjSoY>y1J-{{e}9-j@QB`%3hzVwvu|A zuy>+0l)XixLie|+^#Z{@0+HO_!4@V2**BurKHV)lTdZrXuy*V*ZO+mbBHyDq9iGZR(+I%^iBEX71pdrdzj`K#cwmHYi&Mh+B6;KKME=~a zOw+IAB+29?&#Sv4d##{F$Who{1b@PKw>-x(R`%JQcAwocsSf{O9ZX)+97Q^D9KVf`XTx;s!eUhOs{J^`w({dg#-UqezqeOf4t&e56n^2oQ0ayg z`0c%gh7eKSUDO2@el$!ke@qiTB{)v-IRS2h&KkzPptdmS5o&!+pb~sT@OOf5MIVym zB|$`z;MzF!0}VA3{0Q87bd0oBAE-Zi9*Ts6{TB{2htQr8GxiC7VlX;^f7FPeVj@aC z$yXiwle9D<_1k2O47HIi+JA0bPx^g%@(1?fBaWNOkbiDZX}}2 zQNHl2&vLa`LnB(Kp_P-gU?KvC7DxEpzm05c^N|fi-_A3>q&|w~B3P*5rEXVc?F3h4 zl}`;7?TMHLHNPMpVVa>jl71VGtKlxZ=4jreMo_S^@sh7|(?xhmD0TQ;vt7QqqkVEs zYnnNt4?OzySr{SA6Vl#1!X9ll>d*?;NqT|(KQD#sw$iN=Uw9@LUrctKc|TncCXit9M2IFOLajVX;LE?uHO1g$Z)d*i zOcMX(yI0f1gk+kKL=(ubTj-Oya<(hP2lk)6!xT&Y-<*t~owEe?G?#wbVG7!N?@w_i zalX^}mMiJ}+)txnEAMrFn`VH-yX@Yo=^jk-##%L)A8)kMDUO02Zdz>i%ioFtO-|E;??fg**7 z92SCwtYaqf3L##vlWcLcBdyt)?dRoJ<4v?f>?EIgH4X1A%dV!Ga%d~?(L)nLsxt(> zx;jb^E4k%*QYR4sZdIR;x;_O4^B1mHBzDD>tOkFiIB>G1Vs5R&%La4vjU>E=jo#pVwB&TuzmT z-i&W6W>r1j$JiiVa5Kf!l~m3qm7}R*@!GujWPPZ8xEfs(%+0n2my@oiB8BxMHOO*mqR+GLA|!Mdt{uE5-mYZ3 zfRL;70O&65HZ)BExF%%@;iD(W4otk3mSA|Rrr7_fbIS1m@y3gur$$b&o*J4NYZf*9$rq3LcDgaKqU zm50C#DRLU}V0MHuc33$Q22&b?;tH1*cWM#Jh(x;~(;2v>5O|9|MTmHtTCupjYPiE* z=knRzF25W+pjw*AAc{dqaeDyuV7I?(FqkRNghPh(y`gD;ID8ML@no(qlpT>U8OjyO z3|8r;p{dXedBzYS*ma#U1t0Z7N$fJbFe)2kVGy{L6R|ML(kA~+I;Kw&kc3Pu`x_`F zagYHa%G5ZRZxY2pm~tu(3g98d91qT95sbm&Gg5u7Cql2o+I#H#f3J%ufPPP^2 z`we-PLJJcnoJK_EAkj#>3vVN3Y^LJNMw82Aw;V|}HXT9xK?L1)f>u&~>;kC|`NJ*s zqSkj7So|TD8DhHT6`fjd1H(Nvb#58$B5a74j8;8`{{@v*qkZ@u?50|VoJn>nWjRm? zGf2NYlbduKujzIm2b%M55ru>jT12(79Kje~5d}XsM>*aVa!r+_XQuLK4y1%&JiuY2 zl9>xbBTm!&GX&*ihUBJ2x$r0yy^9{B-TP=81p^f44O<_2{v8QEN4=)==9X}!Xjh3<0 zFx5E?4_WfZHngl(KzDoE-Z@vA0urvDyc;<1d5a? zMX;xL5lK5-tg#3Y>nH6+ht{L9+Ye+6EtacX>IremgdPwlZ80j9_%3F`pSJMFA!Sbw zxG_|;M~DuByAjH)QrqUV@X+Sq=D&w6%{WUtDQQwYPQ>^h71We_ zNx9Ms^0Ic4-;+1s4{;`4m|6Sr;+88)Q%Nyg)<@AzsSJzR1Y84iT}fX^9Zrw7OeD{B z)S|qkM!IV8vBh`4Rs>wfE_|yH`*O>gM7^9ADpWT2g?xxrKJ1HOV2`os=f03G#qB_X zpk~36a-EVf07w4Y*fe+mydX8&X$g9vWF~@egfgt1+ENe^s74oFmb@LkHLFI4TedT8 zwutex1Y-!dZ|PbT)1Q8O?eB0a)9wVb^qEod+zICzgViw|PHS%aOPIKSb)v<^jYiy3 z#O0_9Z2(oNjOsR)OOQuE2S9r&E~n3j2&xFC5>(Kr$P7;UlqFpirYdSu0MdRt5^vaM z`V_bEC~A>2Gjc-KiQpxYNW_hG^hs)Vd`%Ch5rRwlrokmp2@tMq9|GG=bV+CPmGMI% zFJ=_A?K;t!tCe<10=8S_lu7HUSwg)fl}Bljm}g&!=U?r0A3<2`Tzg` diff --git a/ui/pages/dashboard.py b/ui/pages/dashboard.py index 1ade1960..6294896b 100644 --- a/ui/pages/dashboard.py +++ b/ui/pages/dashboard.py @@ -175,6 +175,11 @@ class MetadataUpdateWorker(QThread): if genres_updated: changes_made.append("genres") + # Update album artwork + albums_updated = self.update_album_artwork(artist, spotify_artist) + if albums_updated > 0: + changes_made.append(f"{albums_updated} album art") + if changes_made: # Update artist biography with timestamp to track last update biography_updated = self.plex_client.update_artist_biography(artist) @@ -271,6 +276,162 @@ class MetadataUpdateWorker(QThread): print(f"Error updating genres for {getattr(artist, 'title', 'Unknown')}: {e}") return False + def update_album_artwork(self, artist, spotify_artist): + """Update album artwork for all albums by this artist""" + try: + updated_count = 0 + skipped_count = 0 + + # Get all albums for this artist + try: + albums = list(artist.albums()) + except Exception: + print(f"Could not access albums for artist '{artist.title}'") + return 0 + + if not albums: + print(f"No albums found for artist '{artist.title}'") + return 0 + + print(f"🎨 Checking artwork for {len(albums)} albums by '{artist.title}'...") + + for album in albums: + try: + album_title = getattr(album, 'title', 'Unknown Album') + + # Check if album already has good artwork (debug=True to see detection logic) + if self.album_has_valid_artwork(album, debug=True): + skipped_count += 1 + continue + + print(f"Album '{album_title}' needs artwork - searching Spotify...") + + # Search for this specific album on Spotify + album_query = f"album:{album_title} artist:{spotify_artist.name}" + spotify_albums = self.spotify_client.search_albums(album_query, limit=3) + + if not spotify_albums: + print(f"No Spotify results for album '{album_title}'") + continue + + # Find the best matching album + best_album = None + highest_score = 0.0 + + plex_album_normalized = self.matching_engine.normalize_string(album_title) + + for spotify_album in spotify_albums: + spotify_album_normalized = self.matching_engine.normalize_string(spotify_album.name) + score = self.matching_engine.similarity_score(plex_album_normalized, spotify_album_normalized) + + if score > highest_score: + highest_score = score + best_album = spotify_album + + # If we found a good match with artwork, download it + if best_album and highest_score > 0.7 and best_album.image_url: + print(f"Found Spotify match: '{best_album.name}' (score: {highest_score:.2f})") + + # Download and upload the artwork + if self.download_and_upload_album_artwork(album, best_album.image_url): + updated_count += 1 + + else: + print(f"No good Spotify match for album '{album_title}' (best score: {highest_score:.2f})") + + except Exception as e: + print(f"Error processing album '{getattr(album, 'title', 'Unknown')}': {e}") + continue + + total_processed = updated_count + skipped_count + print(f"🎨 Artwork summary for '{artist.title}': {updated_count} updated, {skipped_count} skipped (already have good artwork)") + + if updated_count == 0 and skipped_count == len(albums): + print(f" ✅ All albums already have good artwork - no Spotify API calls needed!") + return updated_count + + except Exception as e: + print(f"Error updating album artwork for artist '{getattr(artist, 'title', 'Unknown')}': {e}") + return 0 + + def album_has_valid_artwork(self, album, debug=False): + """Check if album has valid artwork - conservative approach""" + try: + album_title = getattr(album, 'title', 'Unknown Album') + + # Check if album has any thumb at all + if not hasattr(album, 'thumb') or not album.thumb: + if debug: print(f" 🎨 Album '{album_title}' has NO THUMB - needs update") + return False + + thumb_url = str(album.thumb) + if debug: print(f" 🔍 Album '{album_title}' artwork URL: {thumb_url}") + + # CONSERVATIVE APPROACH: Only mark as "needs update" in very obvious cases + + # Case 1: Completely empty or None + if not thumb_url or thumb_url.strip() == '': + if debug: print(f" 🎨 Album '{album_title}' has empty URL - needs update") + return False + + # Case 2: Obvious placeholder text in URL + obvious_placeholders = [ + 'no-image', + 'placeholder', + 'missing', + 'default-album', + 'blank.jpg', + 'empty.png' + ] + + thumb_lower = thumb_url.lower() + for placeholder in obvious_placeholders: + if placeholder in thumb_lower: + if debug: print(f" 🎨 Album '{album_title}' has obvious placeholder ({placeholder}) - needs update") + return False + + # Case 3: Extremely short URLs (likely broken) + if len(thumb_url) < 20: + if debug: print(f" 🎨 Album '{album_title}' has very short URL ({len(thumb_url)} chars) - needs update") + return False + + # OTHERWISE: Assume it has valid artwork and SKIP updating + if debug: print(f" ✅ Album '{album_title}' appears to have artwork - SKIPPING (URL: {len(thumb_url)} chars)") + return True + + except Exception as e: + if debug: print(f" ❌ Error checking artwork for album '{album_title}': {e}") + # If we can't check, be conservative and skip updating + return True + + def download_and_upload_album_artwork(self, album, image_url): + """Download artwork from Spotify and upload to Plex""" + try: + album_title = getattr(album, 'title', 'Unknown Album') + + # Download image from Spotify + response = requests.get(image_url, timeout=10) + response.raise_for_status() + + # Validate and convert image (reuse existing function) + image_data = self.validate_and_convert_image(response.content) + if not image_data: + print(f"Invalid image data for album '{album_title}'") + return False + + # Upload to Plex using our new method + success = self.plex_client.update_album_poster(album, image_data) + if success: + print(f"✅ Updated artwork for album '{album_title}'") + else: + print(f"❌ Failed to upload artwork for album '{album_title}'") + + return success + + except Exception as e: + print(f"Error downloading/uploading artwork for album '{getattr(album, 'title', 'Unknown')}': {e}") + return False + def artist_has_valid_photo(self, artist): """Check if artist has a valid photo""" try: