From aa7af89d133f07cbe720809615293ab705358dd1 Mon Sep 17 00:00:00 2001 From: Broque Thomas Date: Thu, 7 Aug 2025 23:49:37 -0700 Subject: [PATCH] code fix --- database/music_library.db-shm | Bin 0 -> 32768 bytes database/music_library.db-wal | Bin 0 -> 4152 bytes .../__pycache__/dashboard.cpython-312.pyc | Bin 133319 -> 150848 bytes ui/pages/dashboard.py | 553 ++++++++++++++---- 4 files changed, 433 insertions(+), 120 deletions(-) create mode 100644 database/music_library.db-shm create mode 100644 database/music_library.db-wal diff --git a/database/music_library.db-shm b/database/music_library.db-shm new file mode 100644 index 0000000000000000000000000000000000000000..a001519b7fdcb76fb11fdc12772db812adc8a738 GIT binary patch literal 32768 zcmeI)u?fOJ6b9gP11m{uV<&7bcsSG z=p%4LNW23Q55SEFfCoTA;$FcSJCLACaO7so2kV<}Ml-*0`}FdQVC~EGAh;H|^Wp3J zw>Jy*v3&FG(~s}*>mZ0<1;Oy$Lci5+pEu4wSF6^$vU(cW>yIbk33vjYfG6Mycmke) zC*TQq0-nJCBXIWo($%1QcE7(8$2VRcvP*}skbZOn^21+8(HW{*T)^|2BD06H288(9#g#N~XCh7vUzx*vi_lW6maNX@r$!DTEc-2{2>ZajJ7q!$K?Ihn0PY zBLH0_jfT=p)_?2uOyvzQBpWyWF{*1pD_sg=IJi1-YgQ9>xEfM}rz`_BA_bBP5dgyn zB`{PwvQt`f?Mj5B)*9~Ei}0PK*-h5F7bT37xTyuHT*Ya)c%=)vn}iES5wHRr)@A(H zcAEDp80~iV$B(w}b|z2O_e?~d_2&F3Wi)j=ag!I7%u;Qsr@5j;I0Nw9)+D)h@nui# z-6&4xJ=Jl$i*d_YmEl9~pw5$1N`Tm;X-Ms!gd#NMlCl#kxwQZp2zEiqh@{*mvH5T4 H&t^XXqI=~8 literal 0 HcmV?d00001 diff --git a/ui/pages/__pycache__/dashboard.cpython-312.pyc b/ui/pages/__pycache__/dashboard.cpython-312.pyc index 8d0e89d9ce7902b44875e6e48412553bb62b86c7..4c330a213cc399a5c474eba3610070a7255b4f67 100644 GIT binary patch delta 37409 zcmce934B!5)%e^uOJ*jM%p{piCi`U1WKT$dBrI775SFk6M8Xo1%mjkjcry_ag8{Xz zS`_eBuT`m7TLo8Ww80&%)~(fygbtmeqHR@b)qq$P_49wueX}KiZNK05`wx2Z-d)c< z_ndRjJ$HF8?^PeYL!*B^EKDnd-`ah*ZfZ~qd-XBIvo~vvzAL&Zy35pLA~KuICUoxI z8$HyO*p%3n)Re@7fT#YEvo?Q*@>^r9pB3DsC#~&qLbI zlCCLDQ@mA`HkCrA(9W{1sZCS8VbhwXL71*{dRKWpN$6RW()dXG3RoS4~q5kqI(;jVI+-nM$ZmL0?hT&9 zgN+azZBO)v*6~migvQw8{GoI0Q+T);!ebv4q!J#K{>Yl#`2}R!``%#de3IWuv{OXPZa2th%?iv!kuW+0oNI{FS_okQ-^$vS_+RX^l`_ zCu`E$LI8%^B5dmG2z^i)muU-wfCyVSz(|`OVAS=pc49MJr*1M{FWW|%q8nw_NcyJo z$3l@Xyh?R7Vdug~87ZYMLlk4-B#ON@jij-K;l#*>r<3H!ewkI#AYz}*Jv|yClUbAK z`yoz}JG@F=M25sTi10)@oGs2iM|0aId)pPw&WLx4q5jNsyvj z`kXz@y<$%rG_<38W3!akqlCYr)2j9;9QMw3aXOSMmLsS@P>Em$f|&q3n&#%V&K8G* z{#H|~ng$^QxzwdhH54>AcXW3+o14=h%%LH)So?~4Or3U4B}=o?rpGMt%Ungv#?;GC zsC902vP+%pR%f`>8T)O=)w%RrZM;$!E|W1zeiy?=i7EeFCywJ}AFsu8}7FJ&dSm^%qe@&UWMyQ@Bm*<5nUYlrziI#3(q;Y{pcQ$i8)u zD7xpvP$H+*BhhTOlIX$$Ws?U9dvGIEne_KirOp=SjHEjkCa|No6C?fpOcdL>2so%a zOeYsMr_nPJQSAH6NjQ-nEgzClrd>@;w|z}QLjz^%>2v3D>Fy7b#q*I&5JN^#NAjozv3?4jb9~r%04)BS`%(3Eg)%g&Oox^yodIa^>bJ%vDRG zG^=4HGJl89xJ#-JA@8hap|=mh6VBwJXnw<_77Z1ZI(L}WEju21&y2Vn1u zl8V(QULQE?vD8B~`D_ z^yTN6Nl`&)(48ewyax%7k}$~%`}zWf{$DJlpP(#;9N z!x0UGEhi|0ii=}3V7Wz~2+H93Lq>XPYpC28-ihJan$ST2|Iuwstr-S?t}LTDsfp zHjBgA-wB#_AaYfQV^e2`!)ck*v$eakr^V*zxDLkijvGO}x8%WqPY*^l5Q7glTF3Q*mQ!OLj?lo~3wtS)OHjX`ZF1pt#H$gn5cDaj}Ibo&tm@ zDTc)5Q+Z-5HYDA2v4W=f+sGCdkOcEUc;j5lQnAM}r^C_P+0uVH5?VCSu4Si8B%3~M z3M-mgR8*<81pPYgPN!YWcl5TjLH_}53#RmL3nZb<#)O#zq26Lw*taC&&0V=vy0q7o}j`8p9Z z&>gWEmr7OUPZd%Db`6+8?9g(O!b}CEP~T~9cUD*cTZ^Nov%|*rMgYEuMYO5pKj3YY z$w|DO?qk=YOLThC^kDAxC--u9fIawAQ9)5DFnEJ0_R^*KNTapjQ=1dCEufI;X_YQ&^s$Hq+)(jJ~itn8k-Bx^T?)4Bn1tz zF7cf?cVocH1E#!0CN4m6$lDo+T?89+}L z+tylMR(i2WP}{|J+q9`gzP82Dp^voW3tDK7)#afM*pE$kOF@^I%l~skE;)YuXNde; zwfpO3r1C#d9aDfyQ#i*O=?;@d1^UoZQi`m55$2Q_j_qF#R(8(`&`{um^0{IL_%+3M{4;@)kjk(Sa=Vq)Sho(7taNwRNi z{2@8{0j~Yvn+mHgcatZ|H2q92*#4kpzix7azT* z1IO^e^Z%GA!I;2MgG9MRE5OW}M3o77p(lYV@&;=;)h4bqDuF7Z3V2``1 zc1HoRkXZIq0ZGpAs8-og+j|r>J)Jg>rp0Dk)nVIccY3t&*yt4P&bCc-C~@tO5ejI8 zaTr`G%k7Si0sG<}n_Y~?vbBK#Plz{n8PDg+i4%-HP_fTwOrZJBZ?-hH=>bS27r+-+@qC(Aiu+7;25Lr z04We#PnbUgf7LfaRh@Q!peOuFyXXYJQRjSn3pCE7g%U37aCEeG+QU4dSlp(bt@FV2 zqGu9glfxlkZpSveZN5Z8d`Dx_n}fzB#TDuy%u5BJt8){0b-*!oh217D!4~Mb^QPJ6 zn^w>tC1ui0Nl8Q45sO;-!J@Nzmmu?m+JneOGCu1<9JYdWdv5fQ@#@PNC~3 z-ldKoO(+^u7t>|Q@!F6C-8or^ChS}!`(qp_ER9XSGwk}X6Y6NU+Tv1M_D7DX)4_*i zO1OP4U4OmyggVl#j&rHw?kd|Kb0GCteEyiafPGv@%7)abP?8!-GRe$Iqe&&lVy0ZL zJE1nZ)d?41ospB$5I+UQ$gpKQD z8k6Lt+OHi`=W=Y&Wwl>7rq1`{Hl|Jm%;HlUr@@XFF%>h%4~$JE(@ zH;A)N$W!D}7acN=si$(Mo>Wf0OPxPfFxy?Q%vG?=U9i?wuy#znE+~Q3HKewVRmbY?wr-GoYiCMrl1sv$}zRotuA$`OAoCaQ&$E>a=aicmvfU;XQoS?IWDWn1|nAg z0}K#ZYQeop*DJ=+#`TNaiHkmu1?RdOH37n;1$` zvlNMA>MWX2r>XS5m_VG4#Zl zC_&u7-g}$m2sUt?q{Zq&_R0Ou75e-Nm`TEUsf9{MpjQ}=wRrmNE<*jwws%egzu7&%C6#F^s5bP$}W+!?0S z+Cm=`=(`!Q=wIbQ;0gXXJr?}p==hA#+QNLucN1IKb_IKL9n9RINEH+r?o6SpGm^cD z!}K=dOT1m_PiTOIhCtoU40z#z$uQ5QVjb`t2%bwv63% zio^@HD0)?9C3s^q=o6Wh;3fScvz*PUBymIMRdaj#x^0#(&}VJ|Yp?AJhoz$%yhC0$ zQ1<(G++(Q*e|#4#F|=6%PI*iPZyQDM(H9gHSiit=>iylrJTW>kyv}=w8%VoY$s#Cs zx-_diR&0fwo=_Nn#s20tK*uSzL5OIhzs#B|H;L2f$61**voVpp)7~vsVdNmb;z*Cf zkVx?jhp@Wt78t>7o{&u~juxj=l-39C#T?sdRrXS{obJtz$~%bZLt!xiJYCJ*EwC7Y zOHCePzi`Z0L`Sn{gf9h#bu0(}#tzxIB9q2h?L(ka4KaJ1?wCAROx|e0{4ss~D>ipS zo2#MC-7xr8!{7;nk?Hs9-G(feA?rZqA)~vv##LN1T2wnOlg%O30{pSo2 zO9|bELYJX%v}pdAp*|?E#APTsRCe4@e#%>F{DsAi*0zskZyYmh8vdpAG7?{(C3}I) z%YZ+xW@#3b%U>;;y`WV7TB&kDIs0Uc#0aklwE9;?ifu$RvAtJ;oz(L>xXHH@y0^$g zXE(!y`W{V~Fi6CHn=mL`NyI)bNphQfklyolG&R(zSd|4_6)1Gnawe25u#gy7K!{>J z>(Kul$qcuX1XvZ(>HBqq#L>;mk%=9H^!e&&@Oy@_m@<%8ztH*%py&Q(inM92m?M+< zZ?Y`PK4% zo%DAr*(%#6UoG28tYHH)m)gaS9vhlOo&6TR4rS?Rx8PFL1*|WvM_GW!AYn`QMzC>^ z#Rl{!C|=$l!j&uWR}d`z8o_-4<_;8qH75CA`+99K-?CL)T)4Qg1jTN`tmsx7DEbd` zw)J%Ng2TiUi763r0jMB^}CsVQy7~R%cF6*q(s-|NpYtG7KsY`^> z4L0|NKG%l6FJb0HmI!C0Kcl*&6RDZ*)N)s9xjS{1D|Obf)T&Wk@_YJ}(adS1vzNOv zhnD-~?Lu(_@%`UP@tC3fq`|~=dv&9Ui;o)`-iyu}&6_z|y~35Z0;+QxtS*Cf%#aJ> zKQvJ(%pEh#o2(Q{-Baecrp!6I#We+WhQH(`#|=xzLuL6Z$!K)eSLecIhS;+*f<)3q z?p}1mqS5%8qs2$lMq}r?b@N=hdEbo7G4{M88K{3vtuSY*>~~62bpiZ+ajK>+MgHP! zb6tY`r37VN3fCW`l~(<#gqYZSBFT|ERt&P8uY;?9!#zaFx}G9g^xf~$P_&~^n6qv2 ziNf5k>{o*98lW=%BP5PBK{x?W?RRc3?aasA2^6HZbo23QD zI?>(^gVv_NatIgRAW%46~1yo zLGj_za!}yXB61l(G51P9mM6_yd2>hmWHEv#}~Qci$+TpjYTaUUAfx5atuDX;o1z44G z$N@hXLBNcJML;Zp|7;=f7Q#{-uuNe2A_@mCZg}cvN+TM7smd_Y6UDHmfXl^E9~i-q zjFNT&WQ6Ro^b5e$1-=4-sq3~AXOZvSyDqL$bM9udtt495TndYOMwLxt(>kZoEiW1A z6ASfpYrYw5*gg6AiPF##00{TpT zZB2q$1r3_a@-YzR3GL`c4?f?HB35In8U$PtxDf2Y*PCfdL0oz*KF&cQ24wNLQg)6e5u@Qspy{Ac{7I;^V|Evo7 zQpxPZRwM+{puwsV19W`^gJ;u)Q)(-@V2;;0pxB=%%?8Ww{nA-vB~2-d6=ukXXOx{Lbo+-% z^zTzmFk>^a{ck|R*TJIX6RlX>+>$^yOw&+BMietN5S^)C1u~Nab@3m_%*^c}P76Kr zY$APXS`NveA57~CMU$O3QM{k7pT0&OB|c7{o?g2ICoV7%=-AlZ(&_MoOEyij*SeBi zPch&Uk{UTN))VRTM)_5$jb@Y=F2oVg6TY#tr?sWCz8f;Z8o9U(5tzt|oB^$BNr!V{ zhWkt8^c0___mn3ma`lc&IB4SFPcn1ffRKS&PTud!Q^Mau@~-o8Y%DFP7>>5ObXK>n z*rh8zl6XwF6}P9-zynljfU|Gh~$H=gdT zj4_o!X$&_Ll0niYY!j}4y;x!*+xZC0MUP$qYe;!&-4(z$VqLcYETxiSEZxy*#3ne= zf5v}C1{ zx<~V89$EWJ?kkC-iK~tqE+1FQ78|O`SEvuUi8#wYKuC(;Q1N5x( zjzqxcnvCjP*@0>T@cCR#jY5uP1JB!IVY(n#Me$< z2^vs-E%qk749wtKZ1(&(Ax{`yGk=vr^)d#>(oYwe>FA;v;1&p5oYjuqE#8g*>l4Ao zl!>1sI0wKJvb9A-%VkoxihsZqIM(~HQHu87&VH%E6L7(z2Z}i^PcPOB^Mv6)E`FXA z)?fkt$~-F3z8RKE4ierr2Z)%=%K~>`e%R;=1Kzt!B84>q{d`HjihC>tHaKNH)Y6VgRp~B ziUY+98-Sh2XZ*)CrQp=D-g`(AJNOS`9@;h4rr0iAEd*&rq=(FF_XP?N<+oKpWWWcOK6MjPoth3x+c3hqE z)wvL_4y*L*u(CrfhZ;v?%cr5G^`221uKD~tCHUwNF;h4@9yLb|xjw3(`k+>fDOGJ`* z!o1s)+w7g4&CS4yoOZ|&RTAN*U{VA_XpJ~J0ep3x?9Yl7e~w9^gQE)LIYqiM@9ZK{ zr%+~}ZBS<`=aI83NVIZ3Ia{DnE+J=4>B`H==Zi?VGXJb4URguVR)i>LosBdr>&V$s zGX|8#D(8~3QzMnD$k}X#()6V+TnXz-07PVZUO&5M9QMD?fnDY0$u`_luI9VMHLy!O zM07){fu(icZG?2svfNT>XQ9pRyoSEDtk?{P3gmVLY^_H_ z7Jg0Yeqt5Qr_->@6E`(V23~_zBPxJOq7nh@mzRmyJsx=n@F1TK{25-wzaV%Uz(JYR z5FBq|!B#3nL?P}&7R-WRAZl#d{@Mdk$71uZ52xFg$A$2=>k0kE^7~dJ-;2ECXX4X) z2)NG0F<#Uo1}EUjH}+gC5${_6XyX-(X>?y>jxZq5R~xIrdj216tWctaNo*op@-vb} zGe0rWho90K;6#96lXoh*!07?|O$ogxllx`q_@h}0@OA2GiY|fvVuhZq*bS~2U*2$= z!be8PC8wv}F%opTn%NV%V2%qbO{sf`nC^@R)cJ4FZ@Sq8bksm>G`9v>Z!hdU1az`( z%8Mlj+fN0HFy1~RGG`)gM8~PKc2HpxVxe?c)If{VUs5>ijQ6LPkYnG!3nuB;&l=gG zP@)a@smAT9L6ul6pd&3BiZWy&@>>?W-HzK>jY}KEL5N4rHKqQaqaey3o{GOe3+9Y4~1?IxjLjj)W-h? z>1N$P{_;L)_U*Gmeo!jBTU~y3lcf>1xwLHpRSnA~+=x9$JmD~7Ywy_D46|{xH2RRF z<9Gk;X3J1D%z63;P_awa5-!B_h4N_nQ;W2+GZ1QN1M9hiTgw}zmYtg2XNITucRyrl z^v(5sh~j#Ud5(9L-h#FaY!3z}0m!lFJRj2lPaH?6l74?#m^Fs8zsSYzb0}1@zVs5# z1+XN8o&<+zBs68!s!g8gW+{6!?mxye9((~w5-XgpdK7pl1de)i+U>p4Nshprira4i z;r1x`h$5K_DEz*u7*sa8DxuI5;w6hGwABs^JzkUYW5kl%F%zd`RDOBRv};f;d<#;I z15=@9AvvWpptsbm%W~>(1`tIj-V4qjOuv46QJR#V5Ptid}KVCyZvdvA|_4IN*GA;NZY$ z<>F(;hR@|feB_>xvuc?!{w~MXg=3em8C~0UZ0K^^=qmfDy?1oMmNBDv60$7VyWn=m&z3-?X*mZ{?#bD+@COPS zU0@qC+A(dGy^T;@Etxzc5>JhJeZc|PEmX4$S4yOXil$FO$8JZh)6p$|CDSELfN_cKV z!_W6q!o#)j|L3Dv*)-X%Ot9mOKBPc< zq{AYtiCMy7#tWKkqCGLg2Gp&ixKak@y71 zH?De>!md354PxoGJGb_TS4f&<2&4m4Nn=l6r^9Z)qP7$F?R&z!!YCEpFpw-+i{Lr* z3OPR|;H#{{Vhy{fv>V&NZCK#&mJR2wuy8R8$40)c1cTy`OoU^NGEaJY2kw3H%A%vW zwZC}_%-cJh{h$o&?RHom0$)q8@d=1a`+_7Tk%Fc2qSLTEgH)c~<||Q>>=5V#n4#cL zGN)9qU?>|XL0e+zqAKWd6nw1rBgrUD)R=xy^w*8))_-A^h0QpLrmH(9*A=RMZH`RYL zu7v1s9cX4;Luy0Fb=5gFF|y|iNKLfj`NA}KejyUWUx*_3JY?iA(flPwQ>&1_V2Q1n zEq|e$;B$o%lg?Jc^Y0XLc!p`hB$F!y`ydgjlk6XE9c5q`5?NFu&;Z~wt15~9ZcNRA z0OIaQgI8|OHl0(*m4$n<&dK1z1{Pe2AQ!#9+ji7wX4G^ zreg?49OlW~2Qv*Tg5$+95&enc6hd#=m|+RDLofLVLW66pBGkPxlhn}v*_fR*AG7(} zC|1L($KcZ{&A8Q{Pa)~zP392p8OtEFb5nvaE)3teX^O%QCzFAUE4#1g2D{|E08`f$ zU~gm3d!oAScALZN??r8bCKB&z#LuJbg?X=2oCyVZod;qSBEyG&(C{+9f|x_QZ;z)- z_N!R-eZ(MC$cI;U-KtPw)#3wmd`m02pEinvC0G>aH}ExcL$Sy-W=8<#&iVgDzb zTE#gxxT}eynarRqjBxIhZcRF5EAyvd@=oQX5i3YO?-Q_G4>g2=EI1?3l zVgnRE5If0T*}JI+Y%^TS5jmzSu#lBh+h0l^r?dN)rSKYY8APIiSCeuYY{Ag&^l*P% z0w>1>I&&i=7|5o3oM|Dvo*&DHBL?;>b&^uQ9<%?JK0jn8E2w8^k#N79X78w0v_d%B z|0}XRwXe&;aq*}mZ!jM>Jj%Xq*aa*83Fsvvw~M^_oQD*6v`soB+<|p* zzR(4A1=7T$w{L@F(08=#+W9FcmJ+|{;0gG}|C2OZ2mQsh1;V}Z;Ww_$P?Np%+)q~~ zVrA#UybKH5)&bKM;8Zw@MYYmRyXL8E_}D}r-<6?Sitln(=W14ZmqQ(6r^I z`$1g#Ui-mN>@IANxLT=#1x*OBuW)bl!y5z2O z=p^S|KPl$2A|a-g5W5{JZf@6C~ia+LWXe2m_4pLHYB(4*+> z0Xah=;v9@`w+Jw>ZqzVA48#nubpVpSVRc2cKpm zKy4^;t+5%Oeu)74QbfY%O3ZQ%zF*Y-$;TK3Zh?}6KbKrg`QXkuI0yqv(gX2Bs0KV? zFy!|3bUW<)ii8GfOI7G(`wQFT(Kk1@ba(fFX#v{K(cCP)i&bPJ`hUZxcM$M)fwx&E zhT|)DY;mc54_^@+RsbUbm(U{*UK@7B<@ z^s|Q&)=Wa_Q>=iupQH2{zVeReRtv{<9K+5a;Al?5@Dq$ca8yCtAD$^;_z9tg_szpE zJY1n52kCc*lcU5kK&r!#1*$2=Ylzaq-$auNTKAjeGEP(M0FN9D6|@h;D?8fI5Wv+$ zP5@89nSr4W@e}&<-;~0ZfH!^+Zwv03lT24h;(abe zh#p9yAsEc;Mu2=DiCuF2&g3bC&+wGLF0cL>svsn z>V>_Y8>&gP6*e+pNkse~EJWG_1^ai=#l#ukZ!6GUzpXZZg)Hhdvy-yX%#P=rOna$` ze)HQyu*mq(h-L9!DC@j3%hK9!@yHu`wl;vpCkHO_M8Wkbus#e^%nrCjr5VnffZ>b2 zDxXQn2MJV1LFmUGStWdRF_&%hQBJYAp`Dl?W(6Q%cuC!q3ocTG+6MW^ui&P27t;gi^{c%q9 z8PgJpB(;rQE2cm$Pc%y&A<;>`DLF6BuK_#?Brq5UKR=oz8L>)%K6|uK&?<*NJDM%8 z!nX4geXB>H^wHmkwz5atW$)^NE#UAh*;~-8W&raNmVZyHDx!u*2eGXizF0PZh>X5WqF>dNYdVozXsWFLH&K=WC4ANx77i!syyFYxgvEhg zT%AS7TQj`Vbz^ZlbR2Bh;8i6%#}=x3K({!4Lf?XIA>-h{%|2mFIbpV*2~osruh)<3 zWTp&vbhaxx`#{X0tYgs?aEdK7`t}v>#9|lx7g~H$7qMr_{(1Y$56nBX;LwcG+3Q9l z*N^F%X~A)GScPlqWus-QM#C>3Q?I6t$NNJzyNZ_`GcBjbj;|*xnc-CuJG}5uiDda2 zK>qYcFp}YBHL0x-B5pu{3&Ub7f;IrY;e|I>1wsZ#Mgl4tS|_f9R0BC&ut(iS;Wj1x zz}*KVUVCDJq?3u5h@~X}2-d8aw;l5q^1Sb#h!yrL>31iR8o7z&9p?^UvIhXb5QXNM z$klDaWOFQFXL1vSE8BRim20C+fp-5nX($T=@)5)!;AUnhK7}KQL;x$GpvDj=5um~o zQFDo?7{n_OV0$^@7q>D$MEf|L*fn*5UF+U%Eki~q%R|JOg0i7Glsh9_69DIDg@GKC{ z2nNj2UL~*s)|6Ioo-BYZw+||ZOHM5(t2wnUu-GrgA{-^o2YjZX#>zDSaDwgI1;G0Q z!M#fQ_Ir7pY;Z72|Nbta67Jl3|L!4PpE9U-u7=bet+T_~36lssz_AdD^EH@TI)m8i z?FcY!P=^IPK)x=6tOF5^V5!!&h&KP#X|JFjDS)`IcF?E(>fIotpsqKJ!@}R5kqhD# z^!X1hD(usNf6^TvMbmFTJTr7Tyq<>LNiuOYHgyd?;iUXLEEe)hXwq7z;J-?76+{I> z+xf5ZG|u&b9rj{Aj(9IVp^+eNK_DVPod**J-gf~Mq~e^ zExZ_2paVTd*g=R>ism-xDO`f#N3VBaHB+G)kG`v?&tYE*CXGYdzvJ~L$gwXPA%(X} zZ_nRMU;89c_*O|ze==0@d-dOpTKV-i$!*0>dh4A_Bj@6Pd%EL;H=S0nNp5g><3ym}o(^x*{TeYX3w zfgboWE#ya-_F93y_2m>5GHuJhXoI;CFBBTKfAvc;kMruq*c6R?&Z`_%bkIpt#fdB{ z^D-%IIByE~bEWuq$V>;$XQ$r@4?fmGJ{0!>2(S)@EQH1;3n8lEGw0U`20pk2O<;!C zes_~FG#gss5x|#v8#ad_cpFxZ{v820*kEowb*jbiA}A#??JbMC}g-b%zw z;_ACFj88`P;nN|A@Xa@a>W5H=!so*iSKPXVOc9o-9>0$$$$I}lEN+1kF2QaAP74gZ z_hN+?Ww$*TgMg@*Mj+)9Q|)2%1@hMd<=fv&V^fb(oe@?Ik+A+5V72QnyJ1UmQ_K& zRRBLi~o_AeUc^HBp25m8OpaF^uyp zmgCV$Gj-_{A6Lj8)nYr`x6~y{T*{`zl4ayh_M=!bEAKVP0t?67D&|~zHx2f zr`Xf!z2ZTBI1;Auo z@6k(Z53D(j6bZ(Vk-cef=ZFsR!;PlSKCzpUP!l_rP97}dVs#KoIgLXtM^AhhUO|X| z3x7PJULV*B9Je2bBq-f%ID@2f*Q+O%g=fM!-G5~e@NkDPYbI%E^UH}rj-$ z`ys2(?Dz|o#Ya(p`hRP7L|1#gHhA-`-}IOg!miFHIUJcbC7K)~FJzNHC?bln3NCS6 zW3QF5yYtEGLv2WBZfcvvsTh!T-ap7IuxEO7?nEV1#vc^J^ExFwn zvYmw_PM8)lvagVwA`1fj9x}P#c}E92|DxUNL2MC}gs_*3$zrK{JtJ`?Gbt+E&a19bmZYuazM`T7FB-Ijs58>62q?g65M&S%V2Y872GVl7(!41&Plqgo>oQhU2ixXy4TczBOllmoZAlIhk+t=CQnPlJcN^H>#1n3Rs z&O`K-{vFB6r=;9F?48(MB(Z;D=e2f21-5?{3@bkePMSq3gq1S31IWtaXOmxLS6~(2 zp+ey0^Vx0~)HKmd3Fk$a*mtvuUiftg3$G%DlDT_-BF`6z$Qdvn{10A1$}rxEX$67#9L?(`SIoD;+1O|`nHK#hQcepc!DT;*Ip|W36IYHT)WChlkz7eX zg!N%n)@ZSpRnLcOf@9fL^GQ##Us-s<+Q1>T4R<;LN<{18xuv8s1Vquu9H0uOP|7zd}ZSx`GhF z+r0ZOBR30`sJr0;RIh^#=bW%+vIdK~6`R7q#k@&t6*(sWRp4 zgY{&246Y4W6$3gx$l5K9{oQR25h9l~6O&*S*v4kkHpFMyHbh$g-%$lI7l#pCg%tSA zPXZ*MGncr)zA z`k8vWxAbnFbSE(Mw#0GUR(cC3y}CfEBgF+h8iWa0`jGU|TW*E>t;e>IloZriZJ=uWKb%b?VxOavj>(*?C50r#5hp4t{u_ji&<_H#_=Cj| zdUQO79ekC9lAqCKB^C;ICdkFBrEGGAQ^Ou9h1(E1pN?YVx52?}-8*W!?#*x_C7?&s z6m(^`5##B2t`YD3(c-N;fl@R03HK|WHex&+3(*RWXtw_z5@S;1l?QN-7J6sk2P;uJ zjckymEXE6)aY+ZRACePij2{D?Ed&j6TNs?3ld~B)a8qyHR7lqp7R7G79d6V5BjjRD zPH)h&ZE!Ww>p5`nB>Cswpm&|VRM*3&3+Rq_V(4|Q^rJKre9^#| z9B$Lw4Dc`FK`#yQ9F8d1BH?DQsO@05Kw~sUXiPNR4>kqA4xoiKHq$;iwvFDjJ()Gb zM>FuYBQg!nm?TEwoki1mn*qQs@to~)UPv8MB*t0kiyHLbZH9|h!Ib)en~cnC^_3)V zXrLHIQ0W#2pGNZT6}RMDCYaL}zS|8BJ4-Txw%_@9-!LpW`5l&)E8!prUabWyUSV*H z#5Oo(h-*!d>pTv0Xia{BYzHT0&+F&3usg4Wvs)&C{r*Z~F}#K%gDfYlqCCp}btNfW ze-DJ5kC2Yl`cIg^9`mH2lhpy)^{dcZ728O% z5tj!hspcB?i)|!(=+CH`Hv&pKWPekK@7CR*8;zO&hnUf2D@RwgAFJOuI)Bq>bjO%_ z^U2W2J*BrNkA+$v+IYz3o;u$(b^hprHDd*9{d>HpKIS|!$33ILHKV~jW4&v}diRVr z*NnDfGwjEvwU18SI2yZYOxNK}e)~+k^eDN~Wvz5u7rLwq-PR>8>ypu>mmRaN8qL0Z zG>J2Ahgotw6xK=xXJW?_o_wP}`x3G&wy zlnYZvjt-EINahlt@aZF91J30>z(7yJ(HA^L^aR|{E7r3&21&*=98&xTOEIIQZwdB8 zE5vy;;HK*8z^l`F&xxGoAG4h8Bqic|q%EYBdCi?#wst$o666~8qwVBy8tS@$cH~

wanPCVjjnJ?S2T6RwjH9uy)QvW- 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 delta 25894 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 0) + logger.debug("Modal UI updated: Automatic processing completed") + + except Exception as e: + logger.error(f"Error checking auto processing status: {e}") + + def refresh_if_auto_processing_complete(self): + """Check if automatic processing completed and refresh the modal""" + try: + if not hasattr(self.parent_dashboard, 'auto_processing_wishlist') or not self.parent_dashboard.auto_processing_wishlist: + # Auto processing is not running, refresh the modal + self.load_wishlist_tracks() + self.begin_download_btn.setText("🚀 Begin Downloads") + self.begin_download_btn.setEnabled(self.total_tracks > 0) + except Exception as e: + logger.error(f"Error refreshing modal after auto processing: {e}") + def start_downloads(self): """Start downloading all wishlist tracks""" try: if self.total_tracks == 0: return + # Check if automatic processing is already running + if hasattr(self.parent_dashboard, 'auto_processing_wishlist') and self.parent_dashboard.auto_processing_wishlist: + QMessageBox.information(self, "Wishlist Processing", + "Automatic wishlist processing is currently running in the background. " + "Please wait for it to complete before starting manual processing.") + return + self.download_in_progress = True self.cancel_requested = False self.begin_download_btn.setEnabled(False) @@ -275,10 +454,7 @@ class DownloadMissingWishlistTracksModal(QDialog): track_data = self.wishlist_tracks[self.download_queue_index] track_index = self.download_queue_index - # Update UI - self.track_table.setItem(track_index, 3, QTableWidgetItem("🔍 Searching...")) - - # Start search and download for this track (simplified) + # Start search and download for this track (status updates handled by worker) self.search_and_download_track_simple(track_data, self.download_queue_index) self.active_parallel_downloads += 1 @@ -304,8 +480,9 @@ class DownloadMissingWishlistTracksModal(QDialog): self.on_track_download_failed(download_index, "Cannot create search query") return - # Use a simple approach - directly call soulseek search + # Use enhanced worker with status updates worker = SimpleWishlistDownloadWorker(self.soulseek_client, query, track_data, download_index) + worker.signals.status_updated.connect(self.on_track_status_updated) worker.signals.download_completed.connect(self.on_track_download_completed) worker.signals.download_failed.connect(self.on_track_download_failed) @@ -315,6 +492,15 @@ class DownloadMissingWishlistTracksModal(QDialog): logger.error(f"Error starting track download: {e}") self.on_track_download_failed(download_index, str(e)) + def on_track_status_updated(self, download_index, status_text): + """Handle live status updates for individual tracks""" + try: + if 0 <= download_index < self.track_table.rowCount(): + self.track_table.setItem(download_index, 3, QTableWidgetItem(status_text)) + logger.debug(f"Updated track {download_index} status to: {status_text}") + except Exception as e: + logger.error(f"Error updating track status: {e}") + def on_track_download_completed(self, download_index, download_id): """Handle successful track download""" try: @@ -419,12 +605,29 @@ class DownloadMissingWishlistTracksModal(QDialog): self.cancel_requested = True self.process_finished.emit() self.reject() + + def closeEvent(self, event): + """Clean up when modal is closed""" + try: + # Stop the status check timer + if hasattr(self, 'status_check_timer') and self.status_check_timer: + self.status_check_timer.stop() + + # Cancel any ongoing downloads + if self.download_in_progress: + self.cancel_requested = True + + except Exception as e: + logger.error(f"Error during modal close: {e}") + + super().closeEvent(event) class SimpleWishlistDownloadWorker(QRunnable): - """Simple worker to download a single wishlist track""" + """Enhanced worker to download a single wishlist track with detailed status updates""" class Signals(QObject): + status_updated = pyqtSignal(int, str) # download_index, status_text download_completed = pyqtSignal(int, str) # download_index, download_id download_failed = pyqtSignal(int, str) # download_index, error_message @@ -437,8 +640,11 @@ class SimpleWishlistDownloadWorker(QRunnable): self.signals = self.Signals() def run(self): - """Run the download""" + """Run the download with detailed status updates""" try: + # Update status: Starting search + self.signals.status_updated.emit(self.download_index, "🔍 Searching...") + # Get quality preference from config.settings import config_manager quality_preference = config_manager.get_quality_preference() @@ -448,12 +654,33 @@ class SimpleWishlistDownloadWorker(QRunnable): asyncio.set_event_loop(loop) try: - download_id = loop.run_until_complete( - self.soulseek_client.search_and_download_best(self.query, quality_preference) + # Update status: Found candidates, analyzing + self.signals.status_updated.emit(self.download_index, "🔎 Analyzing results...") + + # Use the enhanced search method that provides more feedback + results = loop.run_until_complete( + self._search_with_progress(self.query, quality_preference) ) - if download_id: - self.signals.download_completed.emit(self.download_index, download_id) + if results and len(results) > 0: + # Update status: Found candidates, starting download + self.signals.status_updated.emit(self.download_index, f"📋 Found {len(results)} candidates") + time.sleep(0.5) # Brief pause so user can see the status + + # Get the best result and start download + best_result = results[0] # Assuming results are sorted by quality + + self.signals.status_updated.emit(self.download_index, "⏬ Starting download...") + + # Start the actual download + download_id = loop.run_until_complete( + self.soulseek_client.download_track(best_result) + ) + + if download_id: + self.signals.download_completed.emit(self.download_index, download_id) + else: + self.signals.download_failed.emit(self.download_index, "Download failed to start") else: self.signals.download_failed.emit(self.download_index, "No search results found") @@ -462,6 +689,39 @@ class SimpleWishlistDownloadWorker(QRunnable): except Exception as e: self.signals.download_failed.emit(self.download_index, str(e)) + + async def _search_with_progress(self, query, quality_preference): + """Search for tracks with progress updates""" + try: + # Emit search progress + self.signals.status_updated.emit(self.download_index, "🌐 Searching network...") + + # Perform the search (this would ideally use the soulseek client's search methods) + # For now, we'll use the existing search_and_download_best method + # but in a real implementation, you'd want to separate search from download + + # This is a simplified version - in practice you'd want to: + # 1. Search for candidates + # 2. Filter by quality + # 3. Return the results for manual download + + # For now, let's use a direct approach + from core.soulseek_client import SoulseekClient + if hasattr(self.soulseek_client, 'search_tracks'): + results = await self.soulseek_client.search_tracks(query) + + if results: + # Filter by quality preference if needed + filtered_results = self.soulseek_client.filter_results_by_quality_preference( + results, quality_preference + ) + return filtered_results + + return [] + + except Exception as e: + logger.error(f"Error in search with progress: {e}") + return [] class MetadataUpdateWorker(QThread): @@ -1133,11 +1393,19 @@ class DashboardDataProvider(QObject): print(f"DEBUG: Testing {service} connection") print(f"DEBUG: Available service clients: {list(self.service_clients.keys())}") - if service not in self.service_clients: - print(f"DEBUG: Service {service} not found in service_clients") + # Map service names to client keys + service_key_map = { + 'spotify': 'spotify_client', + 'plex': 'plex_client', + 'soulseek': 'soulseek_client' + } + + client_key = service_key_map.get(service, service) + if client_key not in self.service_clients: + print(f"DEBUG: Service {service} (key: {client_key}) not found in service_clients") return - print(f"DEBUG: Service client for {service}: {self.service_clients[service]}") + print(f"DEBUG: Service client for {service}: {self.service_clients[client_key]}") # Clean up any existing test thread for this service if hasattr(self, '_test_threads') and service in self._test_threads: @@ -1152,7 +1420,7 @@ class DashboardDataProvider(QObject): self._test_threads = {} # Run connection test in background thread - test_thread = ServiceTestThread(service, self.service_clients[service]) + test_thread = ServiceTestThread(service, self.service_clients[client_key]) test_thread.test_completed.connect(self.on_service_test_completed) test_thread.finished.connect(lambda: self._cleanup_test_thread(service)) self._test_threads[service] = test_thread @@ -1662,8 +1930,9 @@ class DashboardPage(QWidget): # Timer for automatic wishlist retry processing self.wishlist_retry_timer = QTimer() + self.wishlist_retry_timer.setSingleShot(True) # Single shot timer, we'll restart it after each completion self.wishlist_retry_timer.timeout.connect(self.process_wishlist_automatically) - self.wishlist_retry_timer.start(3600000) # Process every hour (3600000 ms) + self.wishlist_retry_timer.start(60000) # Start first processing 1 minute after app launch (60000 ms) # Track if automatic processing is currently running self.auto_processing_wishlist = False @@ -1995,7 +2264,12 @@ class DashboardPage(QWidget): def start_database_update(self): """Start the SoulSync database update process""" - if not hasattr(self, 'data_provider') or not self.data_provider.service_clients.get('plex'): + logger.debug(f"Starting database update - data_provider exists: {hasattr(self, 'data_provider')}") + if hasattr(self, 'data_provider') and hasattr(self.data_provider, 'service_clients'): + logger.debug(f"Service clients available: {list(self.data_provider.service_clients.keys())}") + logger.debug(f"Plex client: {self.data_provider.service_clients.get('plex')}") + + if not hasattr(self, 'data_provider') or not self.data_provider.service_clients.get('plex_client'): self.add_activity_item("❌", "Database Update", "Plex client not available", "Now") return @@ -2005,7 +2279,7 @@ class DashboardPage(QWidget): # Start the database update worker self.database_worker = DatabaseUpdateWorker( - self.data_provider.service_clients['plex'], + self.data_provider.service_clients['plex_client'], "database/music_library.db", full_refresh ) @@ -2157,11 +2431,15 @@ class DashboardPage(QWidget): def start_metadata_update(self): """Start the Plex metadata update process""" - if not hasattr(self, 'data_provider') or not self.data_provider.service_clients.get('plex'): + logger.debug(f"Starting metadata update - data_provider exists: {hasattr(self, 'data_provider')}") + if hasattr(self, 'data_provider') and hasattr(self.data_provider, 'service_clients'): + logger.debug(f"Service clients available: {list(self.data_provider.service_clients.keys())}") + + if not hasattr(self, 'data_provider') or not self.data_provider.service_clients.get('plex_client'): self.add_activity_item("❌", "Metadata Update", "Plex client not available", "Now") return - if not self.data_provider.service_clients.get('spotify'): + if not self.data_provider.service_clients.get('spotify_client'): self.add_activity_item("❌", "Metadata Update", "Spotify client not available", "Now") return @@ -2172,8 +2450,8 @@ class DashboardPage(QWidget): # Start the metadata update worker (it will handle artist retrieval in background) self.metadata_worker = MetadataUpdateWorker( None, # Artists will be loaded in the worker thread - self.data_provider.service_clients['plex'], - self.data_provider.service_clients['spotify'], + self.data_provider.service_clients['plex_client'], + self.data_provider.service_clients['spotify_client'], refresh_interval_days ) @@ -2404,6 +2682,53 @@ class DashboardPage(QWidget): if hasattr(self, 'wishlist_retry_timer'): self.wishlist_retry_timer.stop() + # Stop the data provider timers + if hasattr(self.data_provider, 'download_stats_timer'): + self.data_provider.download_stats_timer.stop() + if hasattr(self.data_provider, 'system_stats_timer'): + self.data_provider.system_stats_timer.stop() + + # Clean up database-related threads and timers (only on actual shutdown) + if hasattr(self, 'database_worker') and self.database_worker and self.database_worker.isRunning(): + try: + self.database_worker.stop() + self.database_worker.wait(2000) # Give it more time + if self.database_worker.isRunning(): + self.database_worker.terminate() + self.database_worker.deleteLater() + except Exception as e: + logger.debug(f"Error cleaning up database worker: {e}") + + if hasattr(self, 'database_stats_timer') and self.database_stats_timer: + try: + self.database_stats_timer.stop() + except Exception as e: + logger.debug(f"Error stopping database stats timer: {e}") + + # Clean up any running stats workers + if hasattr(self, '_active_stats_workers') and self._active_stats_workers: + try: + for worker in self._active_stats_workers[:]: # Copy list to avoid modification issues + if worker and worker.isRunning(): + worker.stop() + worker.wait(1000) + if worker: + worker.deleteLater() + self._active_stats_workers.clear() + except Exception as e: + logger.debug(f"Error cleaning up stats workers: {e}") + + # Clean up metadata worker as well (only on shutdown) + if hasattr(self, 'metadata_worker') and self.metadata_worker and self.metadata_worker.isRunning(): + try: + self.metadata_worker.stop() + self.metadata_worker.wait(2000) # Give it more time + if self.metadata_worker.isRunning(): + self.metadata_worker.terminate() + self.metadata_worker.deleteLater() + except Exception as e: + logger.debug(f"Error cleaning up metadata worker: {e}") + super().closeEvent(event) def cleanup_threads(self): @@ -2561,11 +2886,21 @@ class DashboardPage(QWidget): # Update button count since tracks may have been removed self.update_wishlist_button_count() + # Refresh any open wishlist modals + for widget in QApplication.instance().allWidgets(): + if isinstance(widget, DownloadMissingWishlistTracksModal) and widget.isVisible(): + widget.refresh_if_auto_processing_complete() + # Show toast notification if there were successful downloads if successful > 0 and hasattr(self, 'toast_manager') and self.toast_manager: message = f"Found {successful} wishlist track{'s' if successful != 1 else ''} automatically!" self.toast_manager.success(message) + # Schedule next wishlist processing in 60 minutes + if hasattr(self, 'wishlist_retry_timer') and self.wishlist_retry_timer: + logger.info("Scheduling next automatic wishlist processing in 60 minutes") + self.wishlist_retry_timer.start(3600000) # 60 minutes (3600000 ms) + except Exception as e: logger.error(f"Error handling automatic wishlist processing completion: {e}") @@ -2574,6 +2909,12 @@ class DashboardPage(QWidget): try: self.auto_processing_wishlist = False logger.error(f"Automatic wishlist processing failed: {error_message}") + + # Schedule next wishlist processing in 60 minutes even after error + if hasattr(self, 'wishlist_retry_timer') and self.wishlist_retry_timer: + logger.info("Scheduling next automatic wishlist processing in 60 minutes (after error)") + self.wishlist_retry_timer.start(3600000) # 60 minutes (3600000 ms) + except Exception as e: logger.error(f"Error handling automatic wishlist processing error: {e}") @@ -2671,32 +3012,4 @@ class AutoWishlistProcessorWorker(QRunnable): logger.error(f"Critical error in automatic wishlist processing: {e}") self.signals.processing_error.emit(str(e)) - # Stop the data provider timers - if hasattr(self.data_provider, 'download_stats_timer'): - self.data_provider.download_stats_timer.stop() - if hasattr(self.data_provider, 'system_stats_timer'): - self.data_provider.system_stats_timer.stop() - - # Clean up database-related threads and timers - if hasattr(self, 'database_worker') and self.database_worker.isRunning(): - self.database_worker.stop() - self.database_worker.wait(1000) - self.database_worker.deleteLater() - - if hasattr(self, 'database_stats_timer'): - self.database_stats_timer.stop() - - # Clean up any running stats workers (keep track of them) - if hasattr(self, '_active_stats_workers'): - for worker in self._active_stats_workers: - if worker.isRunning(): - worker.stop() - worker.wait(1000) - worker.deleteLater() - self._active_stats_workers.clear() - - # Clean up metadata worker as well - if hasattr(self, 'metadata_worker') and self.metadata_worker.isRunning(): - self.metadata_worker.stop() - self.metadata_worker.wait(1000) - self.metadata_worker.deleteLater() \ No newline at end of file + # Worker is complete - no cleanup needed for this simple background task