From 2196ed35f617464fe44fffa77d78c72d031a91e9 Mon Sep 17 00:00:00 2001 From: Broque Thomas Date: Fri, 8 Aug 2025 22:27:02 -0700 Subject: [PATCH] fixing database issues --- .../database_update_worker.cpython-312.pyc | Bin 30191 -> 30371 bytes .../music_database.cpython-312.pyc | Bin 75555 -> 75568 bytes database/music_database.py | 3 +- ui/pages/__pycache__/sync.cpython-312.pyc | Bin 238788 -> 243211 bytes ui/pages/sync.py | 110 ++++++++++++++++-- 5 files changed, 105 insertions(+), 8 deletions(-) diff --git a/core/__pycache__/database_update_worker.cpython-312.pyc b/core/__pycache__/database_update_worker.cpython-312.pyc index 0372f5ce785919d5264a78cf07bfcf19b60e7a90..0b310c7082c1b0b36700d917a203c04c0c81d395 100644 GIT binary patch delta 2729 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 HVs!l0n7utzkWq+@ zv0{6MIO8sMVJTMe4aFZAfYgrS&mii9#dcG9#w|>Y>$bmAWE5s%+`j#vGUGxfo@hx% YzK;wb@ti1(WsQMUzM2@(2bV3eX4|@DQ^> z;xPgNb+dEi(**&Ivq$Rt0|C9WL+;W70m`!$@gM>L){{5$^8wqlEA+7i0pYXV`Q8Kp z1GBvTzXkyow|oTwxCH@3w>1j^2Ll0Kw>=I4Bm)6zw_*_i5(WWxw{;f*x(5>(1sAY$ z@BkSF7r1lt02u`r@HV$X9|5!i0j;<9ApsKt0k*dbBmtoU3|bih4)Fk5837LPdY8B+ M0X_jkm-i+CIjpWr6aWAK diff --git a/database/music_database.py b/database/music_database.py index 0ac752a9..1866ffd9 100644 --- a/database/music_database.py +++ b/database/music_database.py @@ -1377,13 +1377,14 @@ class MusicDatabase: cleaned = re.sub(r'\s*[\]\)]\s*', ' ', cleaned) # Convert closing brackets/parens to space cleaned = re.sub(r'\s*-\s*', ' ', cleaned) # Convert dashes to spaces too - # STEP 2: Remove clear noise patterns + # STEP 2: Remove clear noise patterns - very conservative approach patterns_to_remove = [ r'\s*explicit\s*', # Remove explicit markers (now without brackets) r'\s*clean\s*', # Remove clean markers (now without brackets) r'\s*feat\..*', # Remove featuring (now without brackets) r'\s*featuring.*', # Remove featuring (now without brackets) r'\s*ft\..*', # Remove ft. (now without brackets) + r'\s*edit\s*$', # Remove "- edit" suffix only (specific case: "Reborn - edit" → "Reborn") ] for pattern in patterns_to_remove: diff --git a/ui/pages/__pycache__/sync.cpython-312.pyc b/ui/pages/__pycache__/sync.cpython-312.pyc index 097819a4a9772f0f156b2302163523636dba4a94..350bf7f434e1206ef462841d81b9e9035fffec8f 100644 GIT binary patch delta 12934 zcmbVz33!uL)^N_fNt!lkx~BV*?hRd_$PT5HvdC7TtWvOqCP0VMmN#i>OG^R40R|Ke zdQm~Z1yNjZ9DQ|k1RXy`aY<;goysT(jDu@MTxR&x|D2naqJIDU-}C2rIKAt+=bn4E zd+y6?Z<_z`x!HU&I5~kfDcvt3&h<9}HFMtOytu5wM17G)gmw zurVw`J&*~F;ZU!}js#bL<#y`2gTN9*qZ}jP76->cA?#N_8V4V1K@qP`gtho*)Fha# zm$-I{J0?LAtQAKm!Oh@SLngx$EO{@Xi6IR8sC56Yv3@WGa^a|ImY}&E5`n*$JKF8xRoWnj$^y^ioMF^tWfsiGA_iRu_~i!#XD)1Mf__8v_Oq$ zSP6s5-yqxybxN9sP^q5UXlKe|T7Ey3+yssic!q$7z^l~zDwSR%u#3Q(1WppTQ-vy6 ztWR2pm1fhNrkYCY%sO{fZH-H5rT(2Xd%e1@7FIK8f8^2{VDOmwsSDOIy_W`f#i|Cd zf>+(qfb{C=gevu7X*ooysf}=g!6WKt%}~bRGj(h$9N}=A_;dp#z(x_U5eC7BV#G#B z#K!p>VK98BZrcbi0h|+)Zh=pX=V$>lQShTWVlyZP_)Hwx0Z!}>+leCenW)c8!|L+A@SHYO%Bd95i-h2u$UO+Pkf`oG2t&0-+1uN3=3y9Zxj=(bX%#7SX;cbV za}U7|ZJg|*!QH&Bw$4vgtRzUBdJIayrbZosyO~YmlS|4Ob;QY!e~C9y;uS~7u}H{Q zKYapj)q`2A`4cQchP?eJxCNPz@-}4YkI-_70{#YxOt~udJ`0({`xzs7BDr&oG2H_KRqs zB+fD7`xlW-ABz<)!SJE~c|qO8va*?F{#D}XXozOTCfZ+ue0?--v^hn5^#mlSX@7yg zGssfmI3zP%F6C7;Ip;*_tB{JqwB}WKGXQQ7qu+wH`k}<1VdD9>V4`t2l}TolF~V>X z3iU>6_)JVYiPJ}m#*^SiWM7Xq^poRt`dckZy{7Y zNXRGAG0DweynhGS)*L2vf4RM?+Uc+^uT`vB$^<{g7fwOqQmKk3h_ZzQ77>tUZz2u) zU+iEKjq4QvEJ?cik8`p*tR22*FjwTi5BczpSn@u^#m%KT1xTGshZI}s=Qokl)jQsY zA0TE94Io!gY70{4QAJLfFAjF0QF%uk>-tYv3e|7A;G6$9mV^)f6PAhUtPfzW&UltM zEA4E!`qWwYh$YLGJfcc6E|UgG8!R`!SbgF=l`qgv{ZkG65)!%b7DDwPfpwzbYiNYK)yKYu2%g-6 zZOy^6s_jkHRW7%cj#`@cO9>&2NXFdW)M@_(ca^)^DeP{DQg8hh zxGqilh_E?zweG6rP1Zhay^bP~mR+wt{%=q@(&hLG9)i_s;Lor|A4d}vD=|X)lI^i&| zsZgUnWneey^Ec4MRO*)$j71d9!BWm=DYYvUr|XwaZdeE#4n|2>6Jo9eamQ|SV>?3G zvw(JCd^lUgXZOqOcEY|MvD@_i&|)1m?8Aockh)&{6)T-~hf`7RqORQtTzVv8DM|7; zzbJ1~zp$|90>*xXD|QD~xytHmYN~2hC{ipOG)=k&|LIee5d4Ni;#?w22whA4{qDjn zf|J+*aH>xxv2tD1W#U#Tl2@v1qT)9Mu88DxR%QssnYI|^L(!DZvfu}CFde!1hB%qd zhQWP;Wnj%Lax+*R>{a(=uxB`|5VLJ;GBk^OY-|f05y=Bs0z9gY8NhlN)Qh}4R`XkW zj^wea`hB=obAmWKoLSV+eD)K^*&%~jDm-P1~s#al@J0@<%HF zW`7S3#}m6>{a`p-k0&G+jbtepkhY9u$)J~m)6|2b*h&Z>`&X*Gq=t@Ro#@et0&}j_ zUN~M+7P8zF^MZ{RP5YpA^v1|Z~OD8g9$IDcbp#+!{LE32xjt@EpvIbD}-qV+cPb}Xn>R@K(Ktuv|` zFHuA&&j@D(metxF*11Ym-K7$0egM}cM3ZVO>s{6v^{%Q)Wh2qDLCq~;k2BOg>CE3z zznsW&0mHBO$t;z@AH~=yY!o;|(-d~TO?n#{Mt)9GAl=X#q$odAbuqhm9-(XQ*jxAf6{0f#I_O%tEsJq zz$OAt0(%KON`RzCd4|Aq1nwuWpMX4&<5ZG|ByH@wRDFlQBLt*6%|tGGE*6E!P*pjk zu9?OLgHFb+cZ6Bsgm`&6`~4(G_-4(b=&hRN;E334SQNuFlQoXW%^HWr8G8hA(Qi?l zY>mR!XlMMj)`YE^Mp%?M2Ng=)GJ}$ z2#KDvJA+a}uQjIim0C1?OCXAHPs^wzG42)?DQ+LigT&7dfldU(VYNC4BE|9FLxd;r zHmk{f-E{Ho0+!R?FCLF$X(BHT=M38l5n@9M3*w+@5yw0jd7+r%VP~#4{Mj1H3!TLCg)M>@WARZA04*rW~ow$7;Bxzmt7Vym8lB{LQ%oYzA zEJ?1C>yWYk5m^lTHL~8-Ju}~q7Dr!VfqK@$lwgT)hzoX<)P($sYTVpSni}m|O(VZa zvledB?1CNd_0?U0zk=u)477aynO4R907^ z9kN!{SnKQxl~&g}?A2C`!q!$fRyf_(l~qo~u2imUQVv3)`UiO zhNPVeNjnps&b$}+`CNZ=ZO#IiCAWdZIWeo&d{uUb9!}0HrJ;_Muzg^@*@5!B*srj$W zz#A?6zXpvDy^*mK09ZM}0&g_yP8b=LgMrFsPUT4It?X4=DN6z3=2w_OoidlDnlU!1 zUdH|ot?JZDc9RxwG!Ly{H$$V~E7=HGDaNd1G3ZU_tz@6WT6KIC`?DUp#gDZt+4$dh zgvtoSsB9Bib?7J`7E|lk6u2OE*ReeKO1xUfd}7B`G%{u`d=7{gMPU%3Z3VYZ94`k8 zx3~jHQ!;~UP_0@~@LRcO&4ehX#N(eF;hhPA>8<=am4WLlgI4YZK`)ZjR;@d%uMLe6 zyHDfLw`dUtwD9{h`}9XZEM37-Elut)VO;`|rY7zPY}K{!jYHiLk`S?7VMe^1fAuzt z5%0}}pokW}Dx2~kJK|gPtvb-ufL5Hl!lDye4J|rH;3`bsC|E*UaLo|b6p7=DAxb1z z!0Orci9x&`w0 z+ls)fZ{khdW)gS1*sX2KF;X;A!cygL2l!dh}GT&B)LVnw}s{Qfg;nzArJqGx0`W;*DNI9I3eb`HGH# zGj^Bk(RRiR@Wl-1h|KZMnY%Sm1l6;l#b+X;`#RbqbG8Pe9rK1}9%wqS>Zs*t)6rGl z;$_}Smv@=lYp(AwG@K2I=nP3Y6_Rqs6y^;d+F=?dZmefhgDtV0k-5Ie+^vCK#z?Wd zo|WCuVM;%1j3nfPd@+Mg83)mn$n3+hhmA*vJ|EdRa<*^eZ10FNZ}^kj>HoWg>zji-XWve@{6fHq#q{&++LA1hd)B`jxZ21DwnZve zP>{QWE32J$r2;Q__0?|Iua&?;ia`cUxsj|wHUW}<#XucWMP8%c{&eye)GO~*Us35J z0@HC?Urt$)>tvcxjSIU-zWsN*i{h~+maQ)!G2bJ)npon1D+Wzy^e*?FijI)HKSdm! z+c|WyZ|LOqf*Vg1pDLKqWeV*yrTI*09i|Myn_2c0bcNVvO!FDj9&#NX(wS4}%PDNn z8hgq(_Dt4*>l%)S`Lf27Lr(89hWIBOu%9w!b!BDCR*}=pX68u+$vr%`GjF0VZ=yGM z((z>{5<91q`=*q8Z(QtMywq!7;a$4YYp&`r{7y7CvnFw%7y{uTVf_l?NtlhaL-})n zC*@Nq8q6=)HZk%rX7Z#tUi!~KGjB#)D=qKX7EPm=J&>U!$8duMS?B+``;lo0nRxp! zgbCAY7(5R86ilY;`gMJa>HUjkhdToH>H6FhYKp-YD2}dU>21=KN|RjbY;;>OwyIo3 zhOy6VNx>;>j>_$|vb{FWZ@>!u#x))3`*`mb>x|k4DRi~g8p0)oZxoi-R`{{h%YlBg zU0ki;Wxr`G31_t((=2{b&F&RcJxNy?ZoL$*noy zY^P1>XJ(V(8}Un%*I}A_O;k^tGA{gPNZI_31&h4p6`kd)edVj$7u0y?*LrK~yy0t3 zn-tO3!s6L&8jNl1+_I$+fVWJ4y&$}5Ao15<#tO zL@@WWPjb%BFSsfyTA3}@K@1xAKQKsmTG=$%Dn4pu88TF`u4g(=)`*Dg7Fe|ySB-QB z;57MjplQUVQFbX$OFs-cm{_`=Sv={VMu@HJS%`-lBE^OmP)=W7&*DAzzZfZMQ_$Lv zeLPA;wXq1#%XeMZvb>G0DxmW(MIYOL*0h0czS`cX$Oumwn15rNKe_dz2-v^|fI$r2 z!1Cf()6SkoS~h5|V00Y6XGmv!o-aPHJvv`qzky|F$C6d**1FvIAV5Y?k)}#UjSV|<{fkpkDhV3IHa)NyS z`i6VPeHH^(Xk?TK-^wg-o0`3q{Rl}kyxUaes;IQrR646MpX?{dPk|`e&c>saxOF=l zg!xHzJ9`f-a)k_{P?Fv|^1dA`Q=f{h&Dr7)+gWOWKPGut?b*R@)?!9(<{j)Rz-ckf z!zREsvCD&LsxQT#JxqZwMBZ+e0hwaXZe-$<>iXSmIe`tJY^9bFN~^XuKcGcd-c*jUMJWT1G~Jw+Pd0aQ;*kO$x_Lj{A>88 zEXV6IUWXN@-<-O7gJBGzt7~0tPFx6A@n4tMR=HR9Bet$a?DiGTeq2g4%2NFR+-EW7 z;I1diWnF1+a9Wo+oi%l;V1KZJz&x(J|!r?xvseQGIa< zse5x!f9Q^yZxO{!EGD$y;!6SZTd=kB;sv7Qes(If3l~x*&@_Q8Q!+%xem0lInZ>&Oh&n^` z>}O+Hidh|WfPKfbWS2zBRvs%#9>fG|x>^1DLADo+RzffdL9AKqd6*^Zslk@yfg3Ds zafEj>fzdQdW?E&2^8zg`z5hijk)Kzt5|F{?$5fI#OQC6G`;<(=l1ZgJ0wgDXW4?^+ zxRrY4R+6YBX_~3=oG*$9k-H=?1~YB0dqmA47CWdwmwI_QG=Ofvfw3V1HRjrS( zNIe9Ip--@h@DH)>2{syEQq?Eey=zW0qQ`54;) zo5bVC@ReeVIDd>ShG*4r$JuASPfqU>hfIV{zB|b$s*UQzH`r`IqQnU{312EcJb`KP zE;aT|7Ndt&G59@}56=nvdu&VSuiv4>cki*=W9Wg*MbA;aCa*WxZx#EzY%~iDQMrsa0Us|~Ma>14jhFaE zRu2cofgUylUu;hIuqKAz)Xe;b4b;WYBhd=DX03E;B;CjAM_rRoj>GAfdvzRaWQ zPVs_~E6fn8=9&0UOuv8{7m0!pUZ7t{<)_8U5Wa?4Le&pK_&X3KNtHxP@+HyB*xg#p zX`c$?`7A3`W#N1eB=4r#muR2QQRx99qa0Z)N$ZXg!BIR$d|=^W@G|YvDrTi~y$Fot zVJt6HO^D=PfLFv97CwRv2^CpUd?8#=H$?F{Oxp-bhzMH>Fp$ zWv1}6Ro|j>u@=1&CXPRa8LbUVz$l_(`R@!S<*!7|Yhr6Guhc28V?}%x%M(!L3~@ZK zs5{(IySAnpFUJ^f)vm6qcDkM2VfO0kinaKlbgfD1T(7+P@1`dZQ}YO6-^AnnJE@N2 z(fUGsCUA`t+v51Ssz^<8T4&O5U()cGZg}3^SybjLDr+A-$CotcRMI-kv34$O@GWfc zHm~z7T!-13uEdni#G$^#p)W;{Lzv|&oYjt5lEm3wL+m+YqAz8zyfO^%nFgFT{X1=V6s8ow*& z1Dt}s`tO9Znq+_wD=Vn_A%Wpkl^6crd9|V~oj;TLAt{VZ3i#7#{wZ=`H%*a5ekc}Z z@J#3uZ5ezFzT&@>!5dk?O^CNtd0&jq<`dv1u_2qsjgtEtMGHzn?4r_H1U8eOf=91W z^_wL+viV&7864Zkz$rG|v`ekF@u6CbmLJIDOKcLq%mm6H} z(ceS5Z1{tN7Co^c&j zO*imd4E+z?z!SB&gV%=hPvNY1Y6MTyFD5h>5K2h&c%BTS#PIQa2;48K$Mc!^dDKhe`522Ny`PbiY4IYsgx5#YB6y9E z5+l#+CtCGi;+_&dTmL;#JK9gJaRRr(KOV`Q!1cIf-c zr()YIeiLr){472&w4dj4Gv`I>Y?PW&V&ZI`Y5a<2`f-Sc**rP62OIiwxz{V)&*I71 zd?J*qS7!5VI>fzx0b*$p2N&>J0lhLO@@Dhs1Q4A3I3wc(N6hD)evMAt?`6s2_vJiXOe;Zdu2bJzh`xbg&-ilm zT4%{zTWLTHUPTX-^V6W0Y4azr6~r${&Vr#n(%DqgXqa$3_~OFHmU2114_iZN`W<-Dq*V1fOxo84Pr-Gwsuf3OkR+#O~sK$?q5???v(x*(m zS(Ox&wB}%IcJCE$c~zrL9IE7_X9oMP*HYNNB~u{z8c$YKT0AKW^8BR}zIOiI0hlnU zT3x?dF`{bNbbj@+2(yF7jp&Ya*ShW1cscKVFp*xc+uTP`MT5P%-s%6EZ0R*Y3Db!$ zEvP=Yy_kR_m>G6efoO2>qir3grXGVPIIPo@amti&I86GkjNjzQE~I51aCc^n@@0)` zPa93Xa-pwiVc$il)mPElUcTN}wBDE2<~3QnLeelZ89yND_7M%&TWO-w->>XJ2{F>vDoy7}$#S1!%D}2Qj?ZtNQxMkjDwce<@ z)1hlro0As;yhUyW#d$eO>#OQR%ehStW#Zjxo@NXtz8=GK!cQ0fyBan0D|P&8ehM5i zW|A(TKTyh|<2Xoz4kbF;I zj%cjqC81SRmf;tD{j9tzPS)~~u_<_n-C@^1HeqHJKR>)G66?^WzfDRt#4mj->v$}@ zscx#{qqOj|%#*S|YK66xCr8xQ^k$prDV@^2f#IsI9^;8Pa;}QpWiFZ0{=NAz=%1k# za{MdIYv7?QG+bTLz%T0ba%P1nYvGOhC4?necw2Z1<2vz83o1gcNN(i?EIM2*YvotD z@w%17!i~I=O$%55vXS44uf}4+Eqs=KNjQpDo@m?8tm>a{L5HIMn9!aVA8qCdx-m$e z>Z8y)aC@yc$#2&X38#S7pf z-ASyX<}MzI&)m1%g=mZeX%gbnD1*f1`*?beY#2nXuTd+$tYA=1rL$;F`W{_mEb*l% z-is#d4dL6%59)iH8u#%5Qx_unuM@-RruM^T&37yyc!>YHxd^+It=Qb>M*Q<=J?iqi zk)=y}-N@(S^L@MwUK2(4^Rgi|G)ms&>#5XAKz^pwK&68O&LNS?1_DQ^`n*=`OoZsR zm#Oj!0rFr9MYsyZr;3d2C|*-!yhmY-ax(!65fmU|f2NX#N-`=U%cqdPQNpP77L`a- zls^(6iB_br%M5|c3!KxUvrsNl&*xgthea`>bw3Yex6hQ}gna+ZF zfP7&;fT8F-_0R$S3SdHHTL4n%*ep}&&h{|Baz?Kl7c delta 9684 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 J{J%N~{|AHl`4j*E diff --git a/ui/pages/sync.py b/ui/pages/sync.py index 56965c44..b1adeb48 100644 --- a/ui/pages/sync.py +++ b/ui/pages/sync.py @@ -3141,8 +3141,33 @@ class ManualMatchModal(QDialog): """Initializes the modal with a direct reference to the parent.""" super().__init__(parent_modal) self.parent_modal = parent_modal - self.soulseek_client = parent_modal.parent_page.soulseek_client - self.downloads_page = parent_modal.downloads_page + + # Handle different parent modal types with flexible attribute access + try: + # Try the standard structure first (DownloadMissingTracksModal, DownloadMissingAlbumTracksModal) + self.soulseek_client = parent_modal.parent_page.soulseek_client + self.downloads_page = parent_modal.downloads_page + except AttributeError: + # Fallback for dashboard wishlist modal or other structures + try: + # Dashboard wishlist modal might have soulseek_client directly + self.soulseek_client = getattr(parent_modal, 'soulseek_client', None) + self.downloads_page = getattr(parent_modal, 'downloads_page', None) + + # If still not found, try to get from parent widget hierarchy + if not self.soulseek_client: + current_widget = parent_modal.parent() + while current_widget and not self.soulseek_client: + self.soulseek_client = getattr(current_widget, 'soulseek_client', None) + self.downloads_page = getattr(current_widget, 'downloads_page', None) + current_widget = current_widget.parent() + + except AttributeError: + pass + + # Validate we have the required clients + if not self.soulseek_client: + raise RuntimeError("Could not find soulseek_client in parent modal or widget hierarchy") self.failed_tracks = [] self.current_track_index = 0 @@ -3312,14 +3337,19 @@ class ManualMatchModal(QDialog): preserving the user's current position. """ live_failed_tracks = self.parent_modal.permanently_failed_tracks + old_count = len(self.failed_tracks) if hasattr(self, 'failed_tracks') else 0 current_track_id = None if self.current_track_info: current_track_id = self.current_track_info.get('download_index') self.failed_tracks = list(live_failed_tracks) + new_count = len(self.failed_tracks) + + print(f"🔄 Track list sync: {old_count} → {new_count} failed tracks, current_track_id={current_track_id}") if not self.failed_tracks: + print("⚠️ No failed tracks remaining") return new_index = -1 @@ -3329,6 +3359,7 @@ class ManualMatchModal(QDialog): new_index = i break + old_index = self.current_track_index if new_index != -1: self.current_track_index = new_index else: @@ -3339,26 +3370,39 @@ class ManualMatchModal(QDialog): if self.current_track_index < 0: self.current_track_index = 0 + + if old_index != self.current_track_index: + print(f"📍 Index changed: {old_index} → {self.current_track_index}") def load_current_track(self): """Loads the current failed track's info and intelligently triggers a search.""" self.cancel_current_search() self.clear_results() - # Sync with the parent modal's live list of failed tracks - self._update_track_list() + # Only sync track list if we don't already have the current track loaded + # This prevents the index from being reset when navigating + if not hasattr(self, 'failed_tracks') or len(self.failed_tracks) == 0: + self._update_track_list() if not self.failed_tracks: QMessageBox.information(self, "Complete", "All failed tracks have been addressed.") self.accept() return + # Ensure current_track_index is still valid after any potential sync + if self.current_track_index >= len(self.failed_tracks): + self.current_track_index = len(self.failed_tracks) - 1 + if self.current_track_index < 0: + self.current_track_index = 0 + self.update_navigation_state() self.current_track_info = self.failed_tracks[self.current_track_index] spotify_track = self.current_track_info['spotify_track'] artist = spotify_track.artists[0] if spotify_track.artists else "Unknown" + print(f"📍 Loading track at index {self.current_track_index}: {spotify_track.name} by {artist}") + # Use the original track name for the info label self.info_label.setText(f"Could not find: {spotify_track.name}
by {artist}") @@ -3369,19 +3413,31 @@ class ManualMatchModal(QDialog): def load_next_track(self): """Navigate to the next failed track.""" - if self.current_track_index < len(self.parent_modal.permanently_failed_tracks) - 1: + # Sync the track list first to handle any resolved tracks + self._update_track_list() + + print(f"🔄 Next clicked: current_index={self.current_track_index}, failed_tracks_count={len(self.failed_tracks)}") + + if self.current_track_index < len(self.failed_tracks) - 1: self.current_track_index += 1 + print(f"✅ Moving to next track: new_index={self.current_track_index}") self.load_current_track() + else: + print(f"⚠️ Already at last track (index {self.current_track_index} of {len(self.failed_tracks)})") def load_previous_track(self): """Navigate to the previous failed track.""" + # Sync the track list first to handle any resolved tracks + self._update_track_list() + if self.current_track_index > 0: self.current_track_index -= 1 self.load_current_track() def update_navigation_state(self): """Update the 'Track X of Y' label and enable/disable nav buttons.""" - total_tracks = len(self.parent_modal.permanently_failed_tracks) + # Use the internal synchronized list for consistency + total_tracks = len(self.failed_tracks) # Ensure current_track_index is valid even if list shrinks if self.current_track_index >= total_tracks: @@ -3505,6 +3561,30 @@ class ManualMatchModal(QDialog): self.track_resolved.emit(self.current_track_info) + # Auto-advance to the next failed track after successful selection + # Use a small delay to allow the parent modal to update the failed tracks list + QTimer.singleShot(100, self._advance_to_next_track_after_resolution) + + def _advance_to_next_track_after_resolution(self): + """ + Advances to the next failed track after a successful manual resolution. + If no more tracks remain, closes the modal with a success message. + """ + # Sync the track list to reflect the resolved track being removed + self._update_track_list() + + if not self.failed_tracks: + # No more failed tracks - show success and close + QMessageBox.information(self, "Complete", "All failed tracks have been resolved! 🎉") + self.accept() + return + + # Check if we need to adjust the current index after removal + if self.current_track_index >= len(self.failed_tracks): + self.current_track_index = len(self.failed_tracks) - 1 + + # Load the next track (which might be at the same index if current was removed) + print(f"🔄 Auto-advancing after resolution: index {self.current_track_index} of {len(self.failed_tracks)} remaining") self.load_current_track() def clear_results(self): @@ -4086,7 +4166,11 @@ class DownloadMissingTracksModal(QDialog): self.active_parallel_downloads += 1 self.download_queue_index += 1 - if (self.download_queue_index >= len(self.missing_tracks) and self.active_parallel_downloads == 0): + # Check if we're done: either all downloads completed OR all remaining work is done + downloads_complete = (self.download_queue_index >= len(self.missing_tracks) and self.active_parallel_downloads == 0) + all_work_complete = (self.completed_downloads >= len(self.missing_tracks)) + + if downloads_complete or all_work_complete: self.on_all_downloads_complete() def search_and_download_track_parallel(self, spotify_track, download_index, track_index): @@ -4443,6 +4527,18 @@ class DownloadMissingTracksModal(QDialog): if original_failed_track: self.permanently_failed_tracks.remove(original_failed_track) print(f"✅ Removed track from permanently_failed_tracks - remaining: {len(self.permanently_failed_tracks)}") + + # Update progress bar to account for manually resolved track + # The track was manually resolved, so we need to count it as "completed" + self.successful_downloads += 1 + self.completed_downloads += 1 + # Update the progress bar maximum to reflect the actual remaining work + total_remaining_work = len(self.missing_tracks) - (self.successful_downloads - len(self.permanently_failed_tracks)) + if total_remaining_work > 0: + # Recalculate progress: completed work / total original work + progress_value = self.completed_downloads + self.download_progress.setValue(progress_value) + print(f"📊 Updated progress: {progress_value}/{self.download_progress.maximum()} (manual fix)") else: print("⚠️ Could not find original failed track to remove") self.update_failed_matches_button()