From 86fcdd6869d994f47bfa40045473fcf033352cbd Mon Sep 17 00:00:00 2001 From: Broque Thomas Date: Wed, 6 Aug 2025 19:44:55 -0700 Subject: [PATCH] better matching --- .../matching_engine.cpython-312.pyc | Bin 14962 -> 14972 bytes core/matching_engine.py | 12 ++-- .../music_database.cpython-312.pyc | Bin 50528 -> 51439 bytes database/music_database.py | 67 ++++++++++++------ ui/pages/__pycache__/artists.cpython-312.pyc | Bin 216133 -> 217391 bytes .../__pycache__/downloads.cpython-312.pyc | Bin 510628 -> 512571 bytes ui/pages/__pycache__/sync.cpython-312.pyc | Bin 232213 -> 232052 bytes ui/pages/artists.py | 60 +++++++++++----- ui/pages/downloads.py | 41 +++++++---- ui/pages/sync.py | 8 ++- 10 files changed, 126 insertions(+), 62 deletions(-) diff --git a/core/__pycache__/matching_engine.cpython-312.pyc b/core/__pycache__/matching_engine.cpython-312.pyc index 758e8f7f383cb057a7171c721df0184921cf8ed4..877fd32df47fe8fca26f20affd97dd1c2d915d81 100644 GIT binary patch delta 1248 zcmZ{jUu;uV9LM|X?rpo;c5{CWV{W)mi+5Y+wz@5wXxU^imSw;OVPW06yY8-*bXz(1 zPFa-@V~iLD)Z>c?`alAXH*yk52oMs*gcuVDM#7B``b73%LiU6SiJtGN#2Dj!`1E&v z|IY9CJHM_wkDc%j;c(C+zmY%r>(NW$`}%Wwu_9$Rr(Up*6!N)D&Pf@?kWyswmbsWR zKK0!W1~vtcYd;0**K369dqmX6?*?|#i=w`Kg0{5Fbj9+?X)|7IkM~X)8p92&6G#9r z0p0v`aFB+@ufb`01tF{C>hpOsYa2e+@-JUExNGO=?yZO7PpQLmnW`1^gAzd&X$6|}Nyuzp&ra9qMMaj1dZo~Y`>LEHV9#kJ9 z-)?C3@B_7zbV`WYdu6^6XvX=+b(!Yv5YabNC67Ytm2kC1bHQSLIQIh+qG97lL|=%D z^*6mV!V8UkRfna)C@+;)1t-R@Hhw_gi5;8nP`m?Houy$=*Qh)*IAUH;2CV`I`1Yo0 z`bC^;>eVb&-a+Y7^rmLnI0C#9t|s3x!=@mXEff;DZRGJhIQ==k)g((veRf`bKPQcf zQd2WhsVZ;-^SQjGdQxR1cz>*>ZUSZ#n3Zt-hnLNK&RJodd@Tg&s$oI<*YompbmT$)+-a^A7oQT}Xe z9dX{>xa@zjtLR$M<)?cPtJ?6i|DJ?JKRES{l{FX6RHCUqe#Q)&waG_yO!9D+8VeR)DvG z2yhZO1L%McKzU5_$O*D5e5%LLtM%Z+X7ORqb)p0x?!7_Z@~8SHDl$45>mHCb$dJDP DtbQw? delta 1263 zcmZ{jUu;uV9LIasdvCYe7UGVZn(OM$sf(v4*xDC6HWkag?sy4zCdw(`4o zacd%rCK@m%XbwDxL3v~*CL@Og5`*CZNenL#jAr-2_@r6zLG~gsK6t*T8e@$2;d6fH zch2v3e*f+t?z$Vk9}Wk7^84kfASs2bU=$i*YVFSvi-$f2XdLxabLX%~)Ol8}Dmko)=Dp(AWm zJPK9W%Wy=yacWQk@;?t|u>Ftq5VQlpAU_`&WaHv~WPv>cKV~C!$weI9_!--yeCq%2 zPOk;C7oG+ryognvnxDy4EZ4PZ){*Yh(F3d?&PLmraRAp|<<}ZtVegA4ji;D#7@8y8 zijA?4#O>Jke&ZlCqx|x-`Oaa8$eC2QhamMyc-pKrV^cr&dw>(7BmMznSHz9xuj|-B zUTx`%k~}aQYT-q3$>H}}&a->saO*>s*p5@3CUKyiQGP6JQLDd;tPF?vNZSPaL448H zt1YSU#^jZ1U3Q8_VNfUGX|jiTnt`nu!lxMinagjY1pLE1DImOqzw$~#>nf3+#xa$S> zA>Y*9)C`~0B;iGzIdbjk9NEr%*>&h5ALyQAtzxA+qcy3Lpc%zlzN;A;;{H_o5~{O? z>dNu9U-!PrsCtd8X;SBDi!xur0Itc>`RE-9Ger9#t4gW%Ps*+5FWRo^nir6g zoHU|0pbwEV=lDu0ZhR{DbV@u(^)uEZLh0LeNrXN^DglIO zGP;Q+P#c@iVQ>q06IkHC?JTm>Jh$uJr6d&XfSONCWo;DIFsY!QhfW2(3`zB`4N@2g z0GLto1Dk*<@D>mOE&x{m9WVfdN19JojK1W>9#fC$!C#Z&QqNb6?ck%mci3GX>pQ-| N)|t8P1L-+w@+Sc6FxdbA diff --git a/core/matching_engine.py b/core/matching_engine.py index 56010bb..b6f90aa 100644 --- a/core/matching_engine.py +++ b/core/matching_engine.py @@ -24,14 +24,12 @@ class MatchResult: class MusicMatchingEngine: def __init__(self): - # The order of these patterns is important. More general patterns go first. + # Conservative title patterns - only remove clear noise, preserve meaningful differences like remixes self.title_patterns = [ - # General patterns to remove all content in brackets/parentheses - r'\(.*\)', - r'\[.*\]', - # General pattern to remove everything after a hyphen, which is common for version info - r'\s-\s.*', - # Patterns to remove featuring artists from the title itself + # Only remove explicit/clean markers - preserve remixes, versions, and content after hyphens + r'\s*\(explicit\)', + r'\s*\(clean\)', + # Remove featuring artists from the title itself r'\sfeat\.?.*', r'\sft\.?.*', r'\sfeaturing.*' diff --git a/database/__pycache__/music_database.cpython-312.pyc b/database/__pycache__/music_database.cpython-312.pyc index 4b823b0909e76f3d3c0fae10069ce82f7ea1b7f7..dfb5dc166e2420666cb48bac11ef654d0f980945 100644 GIT binary patch delta 2220 zcmZ`*Z){Ul6o0q>|F`S9&b4D}HwJwh`@a=Ig@GVQ02M@tYZ+y)+sar6_dN#I*HrnS z1x8r8;ur=g!{Ek7vRMoTBF+I4H6|Oyj5Ne7QDclBEZ`@;;JvSv3c;J~+;e{C+~2w9 z-E(g@dr7Ri;o5;}NghD@oK3HkDPB`@j$>AZSgna%qI zqwE=s`6^z;g5DFniWj)B9_3X#a!{nns#f!A!i%vlNk!;#gQ^@*y*DKkmn(cuEJiX) zP}5EYwY&(|(uQl%@w%rg*6MjZuMz0xVXsXmd=CX?U&w2A58v66`E z#Kc`gd0`>3ONebFnw_jQz(xWHXbA?AD#pWhwCwUgj!kOuZS_9r!hD`UZY z60PH)r?nG@bDS6YS!hN#PcJB>2|exr3dS*cQr_c%EuG$XSOd0D(_S+w8C(>)g8lDN zvu11G=CCTIpfovsD-SH0Ehsu=9k50>4J}I)RE5{v)f@X39|}e7xAdiVN-Kvvr(cRz zj~U0-#CMOc8(TVIpR%p!)7~*yW(@XegFRY#+fXyB*7cPgc=7nl(Z+$bzgp*?bm0#H38R}I{o4ha3vAnl8JU8U;hFzYWzJSl)Zre^y(#x^cTFpWb2K;Fo-kI;7#pUI4GE(& zET7ezW1(~HK%Kp*h3MW$-rQw)*GWb8LOr91I$VSWtr)d{@>t90C5h#A90Y$M^{pdX zEfMR9cn#H!_k#`S`|%F2JXZh35~&R?#tvGXG%y~{v)#jbTUcMf+tRfiRuO{DC~|R; zdJ9>cv46VwHh2@uo|0DZH>iuT0o0gMgD-vx!+2p32vG!k93UNC48tVKOC!v#XtAM@1DQE zbM84W@3%4W#;|Dp!eTKIeO@ZPU3S|1&y34PBfqjEkagsp?iW5=i+n#P}_4t-+$ zt|D(id$m4s(j^+c5eNY~{P_BQv6}?wY1(hl49%qDb3*zhICZW>)}(--QRqr;q#_I^ z%N1FZS*0e!t#W6+CPo@KnO&1Lp^q|~CcwpGcC(!1CGb$P#v!h8*cDBQ(UFsyk|p5s zDNTXv?-pB(nh~kEWjae{Gp>=1qzokf0E|9cU>10jA>O1}Gh-+4(6H0mpR46+Iho`N zZr*j=DRZ6%EeFOvTo3c@xq@cG{%FPrSB{%ZBZmz{Gqn-b00ZxDYgWxn;jJgE-dL5l z(p|-ZJ>gfqY9e=4&uW%cEf@&Lx$|DIFA$D;Rj56^ec)~m#vF84c{{QqC-bTq@nP=a z%x2s|E(bM-E%8w?K61h`J1az25yC5I+k~(YRTqX^Sp7X~DOw#IR;+R6^G7;g^2Osp z)+@%C9rmB8c2;75iZH{IR+K&;>x=}C#w=Wex6iaxZ{xl}a>G$H`uvb7Et3N0L}V-i zJ>NOh>kjo>$Ce@UZ8@*dG-OSe>2H4Ux~RC3?GXxpzU6?%vps6|b3)@Tgk@|lg;0yC z2cZt39=<-?M!(lLf3$-Niv+sPH9_ln3p6Avr~rqOrJ3)mimxBnk!Xb9lVy#30nxs2 zZ@0@I?dgg3x_YDGSWuPOc4TZs*uf!T<}(cieQ~ebU6sJmhv2=B?Opc`S^D20tI~Y~ zeT=iMW;zHmJ9oq+{wxAz0R8VXzR=|5l-PolIH;VFct zVd2t*+=}8&I5JoQN2m8tRlhR5Qxbm>$oQO-wn6b+qq!YJ+{n-dzPT6aewd%D6xt|! zwzCN6yoVmp>*rsi&JG@@(rg7A%0g>KQ3bXYzP$XCeK!)-AUuPhAmu?=UMQtM>NgkK z#RqGo(hL4H;nz&yQ_pPp4vOgzg%=h#ud_)77h-9`ubJ2#ywVl=8;b*!`oOwWFLiUu zVf~4vOSCeG?AMUphtdXweW>o`up;@71bs{uSqRFOJyL{w0ldpP^+H~%IQuX3gC}*O vJe!S)g_vk)iTWcfLtcbNtjHqpbE=rW4ARwiEc str: - """Clean track title for comparison by removing common noise""" + """Clean track title for comparison by normalizing brackets/dashes and removing noise""" cleaned = title.lower().strip() - # Remove common patterns that cause mismatches + # STEP 1: Normalize bracket/dash styles for consistent matching + # Convert all bracket styles to spaces for better matching + cleaned = re.sub(r'\s*[\[\(]\s*', ' ', cleaned) # Convert opening brackets/parens to space + 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 patterns_to_remove = [ - r'\s*\(.*\)', # Remove anything in parentheses - r'\s*\[.*\]', # Remove anything in brackets - r'\s*-\s*.*', # Remove everything after dash - r'\s*feat\..*', # Remove featuring artists - r'\s*ft\..*', # Remove ft. artists - r'\s*featuring.*', # Remove featuring + 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) ] for pattern in patterns_to_remove: cleaned = re.sub(pattern, '', cleaned, flags=re.IGNORECASE).strip() + # STEP 3: Clean up extra spaces + cleaned = re.sub(r'\s+', ' ', cleaned).strip() + return cleaned def _clean_album_title_for_comparison(self, title: str) -> str: diff --git a/ui/pages/__pycache__/artists.cpython-312.pyc b/ui/pages/__pycache__/artists.cpython-312.pyc index 86638669c16fe52dae44407fc33cbcd9b4b55190..f43816b0880c05795937f4c34b98977448b1a019 100644 GIT binary patch delta 10736 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 diff --git a/ui/pages/__pycache__/downloads.cpython-312.pyc b/ui/pages/__pycache__/downloads.cpython-312.pyc index 28457d03a6d1176cedb6b383fa17c70aa970377d..04cfa9767bcb63e390a667283c22b59722b6ba3a 100644 GIT binary patch delta 8890 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 c6pm7)agBVAb%7 delta 7191 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 diff --git a/ui/pages/__pycache__/sync.cpython-312.pyc b/ui/pages/__pycache__/sync.cpython-312.pyc index eac4b51304ac9a7c245c2b114f387f093345c84a..43182b4afbc52dc5b07753448857a7e0292319ff 100644 GIT binary patch delta 18244 zcmcJ133yb+vhY-&$z+)XvXGVR34xFRNgxu!CcA7QgaEP`hGYVPY@C?@i9kR>5JZI| zv{wN^y<8Em0;3*5MLo3Xmhj#eM0`YuvTCTa4QXWJ-|+SSxp;q=jx(!%6( z2SlVcb)KI*vRgz@!EaV~lSvJkZ9t{D)8LVBL7pFb9#6Msg5IrW-0zVmCCn6z` z{)q^y7G*FU7WmKiA84Ag7@Gs;Yde;Qho}Nf^dMzm1_f31x#G8$dElE+}Aok zpiFtjtgUVzoubT|Y%)3Ez8q&;VAgUb9L zHE;0qQUxYq8e%F7SYj%(YS1Cu-hv~LaRklxf7G!H@ zX)dIPXh%AH>pGPFiGXjl&6!WZH`=gH6Ma;HGipzEN{b71o2zYAb~PO<)H(!ipURrK zc2(VkW!F!gRPg@;nZL+%F|emr-6hHAXNxJhx3;fKw$DY2DJEX~rON=HpZ!ejGh98h zBEf$+m^zMv-+nI8CS~^+Y98!o+HZ**tXTHP^-IK$*ZYJaJP;a9Fw=)%PEUjfJGVjj zhC(%O`1ud+t-KMEJUCBzBhLrR>Hga4!WivRb`Hd99dp`>exMD``7PibBAS4r-PSb= z-qH3%M{8{+=DObO+8yxoYPXocBh=?8fe%fxVVj84`sc=hRhyc71lf20#BBD)~*bIqa+syS0MhQMq5zXkL0b zl{OQ2oWMB(H3-rp)UH%+nfr|P=S9|{8jH?+Js17fu3qIlTt$}4(8fk^G-*w0{(>y>eJ5R-r!s4k_UNmQbV)i^4( z)rQ}m5)e*B0?%vnZtoA_+RL|}ggEWt38?|?sENQ)?TrcDL_5%4i;i-&nTYBUsg0PF zn8Y2J^=lAox{tA(NE((wAX8g4sRx|U-kkIXeBru(a-GO+PxZ_d?m>OsZAy@dCAF2d z`pOE2v&5;|N@qFLH0sH$i_`K-2E+L$8cP7qYNPwK2}q|l0-tCbr=~(W5*@9boVu1{me2Y&I;bzXH3lw{L!BG#7cmDC;N zsIM+<<{)B6RDB3UYNxC6GP-(Et0!skEehysNNg~*_a|^h>sLJpl3mNH^Mr@jxl~WU zqMfcy6}i`PDt7i{C5&csTWf8qy&9!xn{LNhS2~vmXVE~W;yIdixRz4a)xxCZ;_i&q z=GDc+c$chu5@3|pYwkdps4bqG3Ugdr=JvA$k8B=YO`~(QOA9g~O-o(a3AM9m;Zi8j zK3SL#6SULEVzf?;tzo{acVmtPJEsv6gtRT{*oR5lYv z2bG)zEcBV8^?l$Dn4@icATz26Yu)~~va%7j`kFdty1&7uJGIXr$m#e1H3rP7a8yjI zsH||-vjuVZBaTGxpP^%QTAv5o!^f`j2iF9I--V-^{IJt6J_tVl+tZ0z>JlyJ(E|{z z9es2Fs9MDOB#S|l)_Z-Nbp=tDM3k+RAy12FZQ=S(miwr3j<#cc6s*fBcLka!m6G zViIa08>d38acdDwa~<8fQoso9j%T8SS%xgrLK^a_w(OaNK<1g*F{5U#x?9`%%rqD) z7Yj5?UH%yg_q+Q4^Zp=M>^ic0yALeU?%I1hDB6j=S!pgpun0j@2P8GUr|L@-)BM*8 zNz2~X&(NGKZQj0i!PoGHdfu=7b6+&<(cav5KkRdj+dm3=%)$EfFkXS%ANQ-%R#A=T z@>=TtFao!tBB5JwGPjlOO-ZF~n!OTFt<$d$f;+X;12bW+7W_wyw)H>=Y<9hTpvn(i zuJregncdau=NhN36;$d{>nQHil>+3ULi=+s2eHz*n)FgV1 zb)v&wiKY;j9EnY8ckP!il2chL*zz$!M2p&%np-Tw2(9OrZ3kpgB^?3kgVg5;0Wx&% z0Eg42I!nl^dG(O`I`BW-Wybajz>kJb%&up@jDi;Rr3`d~^?<*HBKvPP(b? zBe0vmdYXr6S%)Q(p~-_YM_X_{wbc={4-VQZi%e&%t+mnrjB&knUI)azxGARFjoMEb zd4erex)CF>p&^l|3@o)(m)a|NQKwxOn<^l)1r^h^iV(a=Ku3_S7|OxtqzSazZ=>v<2~=BvozguUiq&Z*nA)0D z>gOPXPsZ8MoLfnA(&bbiXsez7wH3sG~s*%-U&aXihmFwn{vZ^b~Gdz(8Ja1k7_gY)3#mxn}yca(9wfB9QwUb2j4+F zF@3AY?k*ra_>FWMR8J>+nh6v1XVI(*D3EWNAvcRD*@WE;ubWl-I3ibBiv17)9tdp6 zi?OWEmz{keB61v-9b{^WDfnm1Fh)-Df$lIx*L>itx$!8Wd4y1ZKqaC@ol1aMnjU9t zT$Tm*{Fpkmq4pD0I!PdkK2K5UG=a|uoFTvtT#E{G#K0<%My;$8oo!TPPqmavE%Il5 zA|lj51PtlRBr8Nmqs(tA^6_NLWt9~|pjvOV!Wm&>qA}vaBh%kdOg_>^VU63b!f{(g zS(&}s&^5LdJS+8A$02Cm8&pB<4h-^5&8#HawbkFoNOvm;vfM{o=N>sV6uRn(tznvg zqw?+$=sc!{12nB_qvia+an!4b8PXgIDHh_GVI1Z0R?tcohC%>r&?kjLlTR_Nyg*$- zGrmLMT>>~YhGtY^k-tx+!vylFLBUdb?2WXsw(%OWuRC2xtV@oW4q>u>|H4phMcA+@FNii2$8MY7PND^zx{bPoNKh{shS6sBGle zzOiXzt2Rc2Vas^J%KA=%Q^(0yoe&)`l*$Cg$&s<}G7Odead03wgK+VVd6+tE(@(`g zDoi&%sD>oswpKVwnh)m$8d6AY`w6^G-~fRl1nyQPwf5>VbQDV1e_=Lwt;RL;P=AbD z8tNfMu1tUkIHEtE06PJ`(#IyjMvpR49ZCdOvmW-nz7= zvPR8MOeyETp6cB)xGSjEMcCLBkFv8ZmV28ZUq1LdMC)g|LX)uEOVg_I@GT%4+1+Bt zD+oKg{Yq!pOJ_~1nWy^E%xpEPsKoxgg>-?*zdJj|UIG)E7g8xh z`$BJ!vam12!BV}tFB~u@&%{oH)4}bFC^)!m_CXdIc0EQG4}@;;jJ$UsB*jp7m0~Kl z-x&0-3MpXAr72S6fq{_Mj-|L1JGld=Ro0Zw8dEX7+E(d6pUmzYKn$n2S|$yGJD^f7 z!e9~B=uZrS6(Zpw!ptUrEtS?0xR&@4w?RzUBwV`3R(i;LO4i>7N${-x=xxwO8S@AZ zD;`{s9>hed8wra+A2E<^^kY=Vbhe-;iq*{o9w$(Pz-yzQlwt%-gctNFBVdgmJSs01 z!@;)e5nU52$p%79=CFyPICUe)cg8|&Ml@AFi6A}H2uloAq*@j{8g*|JDI>>0@?XLd zIdmKpMzXq*Mx}d#u5E;2t=v8ia$%+XY8)QwhxD-VFxIE>IU;>KweKLXlK@tkR2P+= zC-4MOip9+_7Tv9LmpEl1qlWqmnRLAg9%4q205BZkxvYnVS-RAT!< z%3~Y`l1h~Xo+7Y~z)AvqHteR-D+HDk*hk=X0xN{v9&Q(}lG$p_LQB85P2 zdB%xcJ)~c8!h8@Lg*+VxQMxty za?w1c`Zlf?Dn}ILaESi|>%0*`24cD{B6v-vE=I%ojvTxg zp0ZL`)k3uVB>%M-M!;cx@LiDP6WN{Wu+gM;F|hIO>n;~AgVHYSr;t(IYUcRGtvD*m z40o8K22UijsQDxbUJiIH%Yn;bMJT&BJXSGsiYap6z33OjNbYq(X50%)LfIYi^fD1C z*Z2BH2Cje-%UPm6OU_;aQyY0D>;`9ZErA;Z&uhvhY#cRor4pb2=V+ZYjYqj`rOt{u zl%?aGGfR@EV%L@Yc0AWGXI0{?L7h-33FVfBE5Q=e5?`=<*>3WtH_O_U(5VYEkrNoB zX>#@_4q}O|>C~xYPL0#I8@mH23es$5i9UNlUE{}Wcx>;J>=`79)bJK!H*Gz6A1rF zDt#iq-3T>!-#XXJcY~@a#_6<)!T1TFr7qSV5y3M6Ewe{)=LJ&pt^4^c8 zlArwc3Fr?8W!95uj3(>%JPAK3aom&exs}?QT4S}Xse1wfQiO2%?l$m~uBY(WN|48& zf(~%!i(AqBr!y5yFC&>jD`zgZOdMuRyrW*UfmFF|8>GYi`p4VgJ)gKD>P|=XR!~`A zv1n@E-mmR|p0HE?u>-BDS?26SHbu*cJ0Z0-g@(WTCYIYOD$#15m8*8b-JN;b8}5+5HI~*?)mGZ+Y*T+AcCqCQbkt!=(%~pkZBDzfb?s%|n;_N- z;ofI(x%7-A$o-puZHL^R#7&8<{9rmLAmEVF@} zeA=}5cdM~+t;Dz_d1eRr!6rTRd6?mYJXy61+8MhgN4~TRYQk^efx%zdYd6GK^XB=_ z)cXu7DjHQ}klf5FR)Vdb>ohX^YH#8c>o`&DAlH*gH7E_j^)w{O?b{&Mf?4hw^@#rU zZfFn*X*8@ofer-H33MdDcPbh3?pJX!nOrMvCom?Cllxyq&+iBQ{HyRg;Br5I4Kj<^ zAWITJi>#aV$S@Jjt87k05ZRoLVfJ31fxAUc-HU<4ANt*UVKu}sRyGCKE04P^t4!xw zwPw1C+HoCjQ?ux<$DJo@_M__(s^7mKs(c__UVaniMr6>A$V5P{Qj5OddkC`HXQSJ% zcA^e>w8k9brgea!ruMS@M3p0fyXBcfkY)kucUA_zg>HMc?C};jkxS3Mg)T&AYUx5C zivVqy0?e5(^IG_JlV!H-5w$Cg06A>0g!`ti4+%qN)` zRfqqXKHqSk+KaTPiugB(hI!aD-z(xS=1@w(5~`5(diFm5RI^USRdrw<-Ef=awg|Lwfwx?MLf3-p?Kp{soU7-YaQ{me0V^Zzmx zvGBU7*dARwRZki6DWt&~iBsLKKlmxEf`R{o^&#Fhv+_FZeY4xT0YgEIS2k`u;?!~!6Jb6vik){xb-WX(QWNli3yznNDH16D$zhEAKzbI zlSZ3F3uk5L)5C}r<+=;#{GXO@U%-6VIsL)~xGZw-qy;@nfP5^K^Tw1xP7nCYH7-q7 zM_`VO{T}inDY@o#=NhQ~P6GGKS>I!3q*!kL9%G5t`b*!#8zRAp$Qy#Z8M8#<2}j0F zu|)kGx#3618M~Cm&&8hURuva;-5oMD{oCM}`M^0E&!NkdP%cmXK&Ji#DRD%%*N9BF zcP>@!=%|#;u-nS)YNlTH6MSVJKn!RI8B<&1tSGNf^wR2y;01AK4uW3-k+O@AJ+vgT z$X<@8+6+$aG^Jpz?eCQST%-s63*Hy0PMV%h`u{+;y!r=3#L(sQjl%PYtjlHSoJKN4 zCA51?8fP^w3>Q;*5p>gJXagrISPYRgs3lQx2Yqtx zkhD}Sp-)y!c4|5EW_Z)YMvnB7sLPyn7LjV1O~tjcqeTpYGFfX8$#9RZSwyxa&%$h{ zc3O#{qjaOu5JW1JSm^OAuFbO0i1qsAK+y@T?=&xHw%)Ckcoq5-P%~kz$h&&=Dk>Ts zKKO{B2IXB*U5x1iyp^oQhar5;><&b(H@B$AWv5{ACH$y|hKLK$_;oT^L3aqI;wx)xd~M+gO4`b-SP>3`wH`MZs@YEgNTz4w-=!>TW)JFIz+N?ngb1QCN)!j-ClHssd{_|@t%NL z@|%vLNBB+8Z<(JV5`%A)rJR`|I$3#b#^Eg=&k)@#3s7Gj$K`9e;#K``nWCSt(7C8i zk!L!Ko|q?!?IL;(ew;cXwWh^}1D+miC{tC zSmcV7X*cB&F2?ZPc#m|W&G-+J{f%6sx=qv?LLe50d1q(;%EQTJx%fQsIemAoxC26) zca2wc<7CI~qBHE#Cv+D87M>znPR$c#mi@%oZZaVs-;#fpCz^a`;e-Y1c8Pg`F!^~8 z(H-8@WAep)kQt6G4XrZqIS;;Yt#($}D)STZgjhs$u_we)47=T*%A-BS?I^sAULpnF z)kpOb77-IeYvJ~tRO0SD@Ohf84Q4Wy^bzUsvHolyw4#OcXst>^>wy)H(waGTHF3P% zKC7}`Wp9G^CpKCK%&4fWOdMA+&F*NLK$Lh!S;yC?v+!Omab(53CQcqT$d_*w9mme4 zsi{)|(%$T3xkFUrplvoM4wd(AvuopZLXt_lSg}}>>1nW~ z#FTJG&QfcO)2ZysP2!cxX9kEw_*uR;KztEJzPsCOpJy-i-bJw%rpQABQF|~^b#%1w1j{x(>23gcaltEA}Pi2WzZydfeEeC4>rrFu+E2X+J0`mw=l5gB5vJJ&ypL##l?jx|Cz(g4`R73}CBqRh@ z>fMH-qT^+QR+tl|&mShNKG0e&A0^_0N6;ikNL=yMU6(J85~K0zz^_o`LJ!%ePzedU>o=3xTA>eFSLwvt{!+E3vQn*<`|Tnx z=K7P_?Z<9^F{T=8Wt(zQoNGmPxGk!ECc1xy_rHcvvQO^0t{|V>LwmVOzjKgsL%A53 zNFr^B9%-wtvsI3?IZJ1ZtSPfqHs4_P&|$iG$^!4pr>jIy(WuDtRpM{3Ltk7iCM)># z<@9WkYUOiwIf+b{L8{n*mr2`H5oCsoa@QP@W;sSpt>w8nA|X{L%6PZ+BOxXc_=9xh zFo6#U?2#Sjinve`vUjV^BMejI__-nq$;D|BDX9Rw?ejleyK7uC>1pR-*27lbxb<;8PO)~*nJ;5Pla6=H%JGq?T^h^64tA9w&g3AEVnJ&3c+ zlL4zmW)Id_jxN4K3f;b{y|&U;YF8VH^5e8Dvja8T?K>N#j29?wpSs#wT;89uX0=Gp z@go_qAI@Qu)wtl|3#IdQjMjUv5et;W4{1`X(}owd z=V~#|(c03=8i$=@^M~bZi|CeMg5HMpOhh+MNAq` z$Vt9(o|OR2zt!WcpP`E5a`a{~1>Jz>Hj6C)Ps(vmh*20MZ+SutgNgEoCy>HOy~C5@ zGC-W(Zi|>pe&U9$qA;D09L^oHnPdyuo^WaeS}tE!$%v=Llh$IA>`3Bafjs!MNPtuF z+|%M#3wz};GAl=k>BbN5+`)XgW-lqLnOjY+xWjlkbnOMrHPg!aZK6MXpue(B{BDM0 zQr|A7TUgt6%hVmh2A|1$c8FTIqF>q}dYhq2&wWu`fs~fq0;d(|or_boqtz)$4`E}B z7m8zF68XV5+F<$EOCqs1ZxT9i+@YSJ-FWZKs>jc{yr(~#I}GG%@!gaW65lB=zl3ga zvCP_q9$yE2!Y*;alEE=A=Q(_cRmQnNrFsMn0p7SD^TzMUn!Om1#K@(4MddZu^&AxQ z-ea3`lOw@MjIk>+N2c$?aAuV(+$Z`Z-)N-W{zR~0o@vkFQ>pFy#9|n(=j<0@7Awm znRPcMRQ~>`^SfV6L_eUZaQ;HWp z67_!IkoHp;V%5oIr$mQP$~Iv}@=d~Xh``%&-zgDpVQwn=$y3518Y>ak;Pix>|73v% z-IPpf2&0X6lu92G_=vz+0!;+Msr73t;fFNcl|CYc{1TGRNh@gHyQ1M3s-xK(m z04?9$2Co(CDk({It-iCdHXccPt*=LSWb9cn2Hbk>S<%n8@gm`-{l*EsI!Zfy{(vj7 z(0C`v&q0mM|DuP~snm@)R)9Zf<5hi3RIp)T+rv@RM(V{6fcdp2t2RI1{FFMoiR55( zgD8R0uQD4`lvR{2y&)sSo-rqePtYiSV|B1v>^MZ5AY zoY;G;(==)r);dsjrbMp~O(ZmQln|t})qXqH8dCHb-(d6#4t>Lq!VIl^3C|JiWQ#%| zkNk)~N-TUYL|Zh%EVsXju@q{lpMK>hu>^!7*MxUKzT6#RJr~#Or8a$E6mfwg)&1V^1a+}>PcE{DD;N=FoJYA=od1I++bFEM6&ia zE2%da|4@EwR@wIRs4Nb~#3EwBOu~limNy+_pR*!C zp7K)yeU1FrBYKQg*$BaG{T?An@)j(XhXR#Z=*4skQUV0tZ}e@g%u>I#j#KJyZHWd~V{bAmyrn!34=})HS0>0)?MWV9 zqkwXByb>PMvNE2S>0q~Qrd$-S6yxjXkK>g+!dUAdq|Mz%CN5Ok$el?_R`?C0dEilb zDM?ucpX*DLm0b#aDf^}=g_b{QMZM+PG^Jgth4h)#VJj{69|WEzz`00r)YVXVI!(#* z$}P6NGRfL9*HL*_du18?q6c(Pd=xk&gEN%yYb3}#J1uiFlnm=?(zPiB;^i!?z>6K3 zp`_j@(JAts3}pet>O(V?WOEm$fq8M7s1Zak;-JJ8|D2j@U+^{!mZR}OnB1PF^oAkw zyDTNb5`(=PhOn(=UsCqXRzmUDEJfK$r_Q{7R-R3Sf+ssqrRzoN4XXZ`t)yAcQTq^L z(ja*{TZxC6@=CTc5<2O_bClnFW0+W`?JD(Tih;;JEvMuw-IFd+?au_hL{Q8Rsg1`d z>K{~Vk}u>dsgNbz`AQ>PmJ@p_gM(WH#WaSjOO&b5PEVbxd=J(Zv;U)!2+WzL zvwH3;U zMiQbsn0^|s8h0^x-+}p?J4kmwAt`@Kpk-&=uz3hO|1S*OmgyLVW9Uv=V?=h6N_>@5 zK)osm3?+%TMu0cBU9mmWqeay92m-f{)8@e6<+Hvq{WGYEPG@zNoIX<-2E+87GZm{j zxP=v*s@Wpnt439uh&>zHk&6-=&>VCx)Z=QD8O_1<%35Wn?ZJ??7TopzyP#hf$|mpE+}`QGRZuZc6NhL=Wp zRGEu8gW!9VP62VG&II1mhuoutgCCXC3)JCy*)k;v;J8$mD;4<9$>mDskaviT!vww{ zz@|BzkhUX`P9TRsE`gy0IMIHHCZQaz`Yvt8I63NG<%?DW1;lMi|Jg+U{#+n$U7>vZ E-%=9li2wiq delta 18298 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 UK+=}1UrhAxFZao(S14coA08zELjV8( diff --git a/ui/pages/artists.py b/ui/pages/artists.py index aabbe0e..76bf2b1 100644 --- a/ui/pages/artists.py +++ b/ui/pages/artists.py @@ -1047,9 +1047,14 @@ class ArtistResultCard(QFrame): def mousePressEvent(self, event): """Handle click to select artist""" - if event.button() == Qt.MouseButton.LeftButton: - self.artist_selected.emit(self.artist) - super().mousePressEvent(event) + try: + if event.button() == Qt.MouseButton.LeftButton: + self.artist_selected.emit(self.artist) + super().mousePressEvent(event) + except RuntimeError as e: + # Qt object has been deleted, ignore the event silently + print(f"⚠️ ArtistCard object deleted during mouse event: {e}") + pass class AlbumCard(QFrame): """Card widget for displaying album information""" @@ -1485,12 +1490,17 @@ class AlbumCard(QFrame): def mousePressEvent(self, event): """Handle click for download""" - # Don't allow downloads if already downloading - if (event.button() == Qt.MouseButton.LeftButton and - not self.progress_overlay.isVisible()): - print(f"🖱️ Album card clicked: {self.album.name} (owned: {self.is_owned})") - self.download_requested.emit(self.album) - super().mousePressEvent(event) + try: + # Don't allow downloads if already downloading + if (event.button() == Qt.MouseButton.LeftButton and + not self.progress_overlay.isVisible()): + print(f"🖱️ Album card clicked: {self.album.name} (owned: {self.is_owned})") + self.download_requested.emit(self.album) + super().mousePressEvent(event) + except RuntimeError as e: + # Qt object has been deleted, ignore the event silently + print(f"⚠️ AlbumCard object deleted during mouse event: {e}") + pass class DownloadMissingAlbumTracksModal(QDialog): """Enhanced modal for downloading missing album tracks with live progress tracking""" @@ -1978,7 +1988,10 @@ class DownloadMissingAlbumTracksModal(QDialog): else: self.download_in_progress = False self.cancel_btn.hide() - self.process_finished.emit() + try: + self.process_finished.emit() + except RuntimeError as e: + print(f"⚠️ Modal object deleted during analysis complete signal: {e}") QMessageBox.information(self, "Analysis Complete", "All album tracks already exist in Plex! No downloads needed.") # Close with accept since all tracks are already available (success case) self.accept() @@ -2384,7 +2397,10 @@ class DownloadMissingAlbumTracksModal(QDialog): self.cancel_btn.hide() # Emit process_finished signal to unlock UI - self.process_finished.emit() + try: + self.process_finished.emit() + except RuntimeError as e: + print(f"⚠️ Modal object deleted during downloads complete signal: {e}") # Determine the final message based on success or failure if self.permanently_failed_tracks: @@ -2494,16 +2510,24 @@ class DownloadMissingAlbumTracksModal(QDialog): def on_cancel_clicked(self): """Handle Cancel button""" - self.cancel_operations() - self.process_finished.emit() - self.reject() + try: + self.cancel_operations() + self.process_finished.emit() + self.reject() + except RuntimeError as e: + print(f"⚠️ Modal object deleted during cancel: {e}") + pass def on_close_clicked(self): """Handle Close button""" - if self.cancel_requested or not self.download_in_progress: - self.cancel_operations() - self.process_finished.emit() - self.reject() + try: + if self.cancel_requested or not self.download_in_progress: + self.cancel_operations() + self.process_finished.emit() + self.reject() + except RuntimeError as e: + print(f"⚠️ Modal object deleted during close: {e}") + pass def cancel_operations(self): """Cancel any ongoing operations""" diff --git a/ui/pages/downloads.py b/ui/pages/downloads.py index 435c917..2e9f5ff 100644 --- a/ui/pages/downloads.py +++ b/ui/pages/downloads.py @@ -7542,10 +7542,12 @@ class DownloadsPage(QWidget): print(f" Album name from album_info: '{album_info['album_name']}'") print(f" Original download item title: '{download_item.title}'") - # Use clean track name from album_info if available - clean_track_name = album_info.get('clean_track_name', download_item.title) - if hasattr(download_item, '_spotify_clean_title'): + # Use the Spotify title information if available (most accurate for matched tracks) + clean_track_name = download_item.title + if hasattr(download_item, '_spotify_clean_title') and download_item._spotify_clean_title: clean_track_name = download_item._spotify_clean_title + elif album_info.get('clean_track_name'): + clean_track_name = album_info['clean_track_name'] print(f" Clean track name to use: '{clean_track_name}'") @@ -7564,10 +7566,12 @@ class DownloadsPage(QWidget): else: # Single track structure: Transfer/ARTIST/ARTIST - SINGLE/SINGLE.ext - # Use clean track name for single folder and filename - clean_track_name = album_info.get('clean_track_name', download_item.title) if album_info else download_item.title - if hasattr(download_item, '_spotify_clean_title'): + # Use the Spotify title information if available (most accurate for matched tracks) + clean_track_name = download_item.title + if hasattr(download_item, '_spotify_clean_title') and download_item._spotify_clean_title: clean_track_name = download_item._spotify_clean_title + elif album_info and album_info.get('clean_track_name'): + clean_track_name = album_info['clean_track_name'] print(f" Original download item title: '{download_item.title}'") print(f" Clean track name to use: '{clean_track_name}'") @@ -8098,8 +8102,9 @@ class DownloadsPage(QWidget): track_num = spotify_track_number print(f"🎯 Using Spotify track number: {track_num}") - # Store the clean Spotify track name for use in file organization - download_item._spotify_clean_title = clean_track_name + # Store the clean Spotify track name for use in file organization (only if not already set) + if not hasattr(download_item, '_spotify_clean_title') or not download_item._spotify_clean_title: + download_item._spotify_clean_title = clean_track_name download_item._spotify_clean_album = album_name # Extract album image URL from detailed track data @@ -8147,7 +8152,9 @@ class DownloadsPage(QWidget): # Get track number from metadata or filename as fallback track_num = self._extract_track_number(download_item) - download_item._spotify_clean_title = clean_track_name + # Only set if not already set (preserve original Spotify title from modal) + if not hasattr(download_item, '_spotify_clean_title') or not download_item._spotify_clean_title: + download_item._spotify_clean_title = clean_track_name download_item._spotify_clean_album = album_name # Try to get album image URL from matched_album if available @@ -8175,7 +8182,9 @@ class DownloadsPage(QWidget): print(f"✅ Using cleaned Soulseek album context: '{clean_album}' (cleaned from '{download_item.album}')") print(f"🧹 Cleaned track title: '{clean_title}' (cleaned from '{download_item.title}')") - download_item._spotify_clean_title = clean_title + # Only set if not already set (preserve original Spotify title from modal) + if not hasattr(download_item, '_spotify_clean_title') or not download_item._spotify_clean_title: + download_item._spotify_clean_title = clean_title download_item._spotify_clean_album = clean_album # Try to get album image URL from matched_album if available @@ -8196,7 +8205,9 @@ class DownloadsPage(QWidget): print(f"🎯 No album context found, defaulting to single track structure with cleaned title") clean_title = self._clean_track_title(download_item.title, artist.name) - download_item._spotify_clean_title = clean_title + # Only set if not already set (preserve original Spotify title from modal) + if not hasattr(download_item, '_spotify_clean_title') or not download_item._spotify_clean_title: + download_item._spotify_clean_title = clean_title # Try to get album image URL from matched_album if available album_image_url = None @@ -8273,7 +8284,11 @@ class DownloadsPage(QWidget): self.matching_engine.normalize_string(track_name) ) - if similarity > 0.7: # Good match threshold + # Use higher threshold for remix matching to ensure precision + is_remix = any(word in clean_track.lower() for word in ['remix', 'mix', 'edit', 'version']) + threshold = 0.9 if is_remix else 0.7 # Much stricter for remixes + + if similarity > threshold: print(f"✅ FOUND: '{track_name}' (track #{track_number}) matches '{clean_track}' (similarity: {similarity:.2f})") print(f"🎯 Forcing album classification for track in '{album.name}'") @@ -8282,7 +8297,7 @@ class DownloadsPage(QWidget): 'is_album': True, # Always true - we found it in an album! 'album_name': album.name, 'track_number': track_number, - 'clean_track_name': track_name, # Use Spotify's clean name + 'clean_track_name': clean_track, # Use the ORIGINAL download title, not the database match 'album_image_url': album.image_url, 'confidence': similarity, 'source': 'album_context_search' diff --git a/ui/pages/sync.py b/ui/pages/sync.py index 48c57cc..06a2f95 100644 --- a/ui/pages/sync.py +++ b/ui/pages/sync.py @@ -200,18 +200,20 @@ class PlaylistTrackAnalysisWorker(QRunnable): # Get database instance db = get_database() - # --- Generate a list of title variations --- + # --- Generate conservative title variations (preserve meaningful differences) --- title_variations = [original_title] - if " - " in original_title: - title_variations.append(original_title.split(' - ')[0].strip()) + # Only add cleaned version if it removes clear noise (not meaningful content like remixes) cleaned_for_search = clean_track_name_for_search(original_title) if cleaned_for_search.lower() != original_title.lower(): title_variations.append(cleaned_for_search) + # Use matching engine's conservative clean_title (no longer strips remixes/versions) base_title = self.matching_engine.clean_title(original_title) if base_title.lower() not in [t.lower() for t in title_variations]: title_variations.append(base_title) + + # DO NOT strip content after dashes - this removes important remix/version info unique_title_variations = list(dict.fromkeys(title_variations))