From 852e755b958502bc4c0e31a9a72f9269e0812930 Mon Sep 17 00:00:00 2001 From: Broque Thomas Date: Fri, 25 Jul 2025 18:04:23 -0700 Subject: [PATCH] progress --- .spotify_cache | 2 +- .../matching_engine.cpython-310.pyc | Bin 0 -> 9976 bytes .../matching_engine.cpython-312.pyc | Bin 11256 -> 14797 bytes core/__pycache__/plex_client.cpython-310.pyc | Bin 0 -> 15753 bytes core/__pycache__/plex_client.cpython-312.pyc | Bin 23937 -> 28845 bytes core/matching_engine.py | 106 ++++++++- core/plex_client.py | 101 +++++++++ logs/app.log | 206 ++++++++++++++++++ ui/pages/__pycache__/artists.cpython-310.pyc | Bin 43987 -> 45530 bytes ui/pages/__pycache__/artists.cpython-312.pyc | Bin 80241 -> 83331 bytes ui/pages/artists.py | 104 ++++++--- 11 files changed, 492 insertions(+), 27 deletions(-) create mode 100644 core/__pycache__/matching_engine.cpython-310.pyc create mode 100644 core/__pycache__/plex_client.cpython-310.pyc diff --git a/.spotify_cache b/.spotify_cache index 20b48515..2fdda7a6 100644 --- a/.spotify_cache +++ b/.spotify_cache @@ -1 +1 @@ -{"access_token": "BQCOv8fOrv4GnSSOovKVMwFz8YN_6oKKiuxjvfiS2m_jhbXRHKwiuL2JkoFWRwtNRjOcJ8HLYBboNNColGWmPtH-jxxSp1dQcn0Ln-vJC6R8MXbjVqiNlVgALF1UaK4-KKzbHaenIy7tPx7BMbZrwhBErrouJttReJB3jqLHGqRAajZI7A3un1x5wC5qAPVHkiI3aX2Z3VTkke5WVzChkt30vrlArNY72K9XXhKZTC4qiUuqcq89tLiusgYLmhCP", "token_type": "Bearer", "expires_in": 3600, "scope": "user-library-read user-read-private playlist-read-private playlist-read-collaborative user-read-email", "expires_at": 1753491117, "refresh_token": "AQDmfQkPCGObfJeTUIbW1hAAwhSqkuHRA3Qh2dqVYMRh0eCkFMQgPNJDDzF8y-BiaVbj80zePkK_XSfYH1aJutMtNbnsqRKWuxP31BTrMc7pdUdbE7Fma4oH8wpDUKdG3MM"} \ No newline at end of file +{"access_token": "BQCCZ_QN3sFdtefCigaIuPuMYIdP2K25HRJ-NAQs1yLwHQkj9YxyvN3zAUEj6A3kANHI5yqH3DGN7oRzAO778PTcxQwcFFbpOWAKgi80owbwXVva_SwcjBL3MyLEAQJh2TN3iolRTWycnfHx0LSvT2Ddkm6o09xjQfxIfI14t5dEwummt4Ceqb0AykByuhtEpSCzjh6Z0cfTayvOYPv_1lgm2JSLalKSaz3osh9PhT-PRLAmbA-F1yCaTs73bnhc", "token_type": "Bearer", "expires_in": 3600, "scope": "user-library-read user-read-private playlist-read-private playlist-read-collaborative user-read-email", "expires_at": 1753495023, "refresh_token": "AQDmfQkPCGObfJeTUIbW1hAAwhSqkuHRA3Qh2dqVYMRh0eCkFMQgPNJDDzF8y-BiaVbj80zePkK_XSfYH1aJutMtNbnsqRKWuxP31BTrMc7pdUdbE7Fma4oH8wpDUKdG3MM"} \ No newline at end of file diff --git a/core/__pycache__/matching_engine.cpython-310.pyc b/core/__pycache__/matching_engine.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c6dac3e1bf0e1a3d569918a91c4ab9577022307d GIT binary patch literal 9976 zcmbVS+m9S)RrH&LS9()2ivOX54ml zb^EL8*wb4rf$W8Si69a$yAjA{dBEavB@llC67YfqqIf~!q2uvxUK6=7V_ zT8&58sn#l_)(^T-oH`#2;%-0mg4DXy?Zl~hG2BZFp9}|qf4QZnCE>+hC-9;Og~k>C z*3b_-{`+3sx$aA9ABJ7wclyFdp}gzI?V!KA%T0w(q}REL>e`2ae}@b2g*$yzRJic5 z9}R;zZS-ieZn)d_!`*J^cW8UFp9zUCq1M+nb)jtFQM_k?AQj<&1f{gXC&+eCiw6C;yR+AhX~St5N57NjJNEAoS#hnieP@mDLIzVu(%N1FA)Ug~tCHdh}(w}{qTHb%=c$63!iTy9lU zr`-;{p5Jb#<#xN*7sG(^)pq;V&J@c-zLd1lZhK+ak3HgwsNI$(mXK8>X<;Ym zd*F~LmZ>w4{edszz0}(7_XD{`y=dn%$uTN6kks*wC@E`>QPmy%8OE~CZI4~IgMJri zfB-@B>lV5&?rEE*pi8%WYsV4=Vc)Yh3o|uE;h@GArAeQ%sGz1Os*@U$25Zw1jY&;Y zETE<&7E!arwdEaKEQ=$kt%#$3RjlA^fsJgjDvqJHCf5AAc*1X>w&|C#8{?RU*5l$y zv@US#34hUV9MjNa2`x_v7cI-&qIUl+@om%`;hLxYquKn=h-Xo|!nMy~hNC3ox)%}H@Xl=?FoP%6qGV^lF9@$%`)=zJ>@VtuWwNY^^I=dCE zpB`1Vq8;Chx7IgMD4~#LYpQ-IvHbdJ%z%!kRZrFNH1$Nbp4DePu|jnEIn+CNdZ9qh zJc9|0v>tb3F#EuZV_$|*+VEryeK)C)M{$aM%u3?Y4ZCr>P3#gf0ZgSe&j+FK{0oG&xNA;4Dv{&xbI%P^45FropFQyZC`f0 z2(@QikjIsNum2fJlewaS-|6mjJMLf@cH$vRy?gt5H}Kt?zCWPpykKzM3x_>lqFd*> z2PFxTM(dL~Qf=g1{dDU#F8SKX+=`z6mF~6*vV=3I1>QnxNFRD*xSbYg$AgE4TtTZm zMafiakitltVSc=}I^Bz?W$j@VEZs32y>2Y(mN9zfpj+8L`0y+FHm04wlE<%u_<3~o zW7hSN>$xXME1z^h=m^@;?eCy2yMo)@_`2Hf&B_xSHHVDivY1iswGuCeyi3xr+kOSv_ZZV%4 zw+e}NzmOOYh+oao_umS9FN|ECpExw~Bv@bTMkUA*$PT&JdUNI**FF*-n6?9gU>krp$B zu%DV0?$GAVzK0QCD@vA)HN9+%o;rwnWGCCQB-%A5O|I>r1FN+ z7q04{n5ZAa)^fvsH}VxJ8PY`?#p8PNA0jm^yq1 z$;dtYz^ZFn_JZwUkFATjQniU=z{PP1GZiIdK_!ViXbeAgFUHek-Ue%w)1Yc)ozK#8qWstdAN zrwt(08n^g&29R1`%(pFa8`#&2AlSa3{*AdhR6NNQFrB)zA5ZSh&vA^KS=)NSeA5YT z+U|$L2sySv@yt$znVa0cK2rmz)cyy8c%jA!acD^z)EfAv;NF=63w{wpZ?djmYc{Fl}h%jA%JAU`%Uew;AZRSZ|! zA8O4q9Mf53kvvfQ?;i}CI+ibT`clWlO!J=G1}UesEbWPoOL{Ev<9PnIU7|K4~+JBO8LdfQQI_Pjf}wtpM-@*N~; zS#)=H+DwG~G+xTrYES9*2N)BPo6{sRo0fhQR>tV$L3;3j(XvVFk~cTUX5`t?PKn(j z3+MSGZHMS&+;bA0@Dz5S`4jCvsCWaoIku2DvphVTvCWEdbzDq}^0}m#Sf6R~Ibn@$ zT?=dIYoV_2z!*D;lh`*c`BLJH?OSV!JuacOfHiE)TIRfQqlkPZDa((Na#Fz!e`b7PIpDbi+3iElVCf1M&Z{4sfqW7E zz*t~=%VT+iNtGF9%GN+=qH}Y7&Nt7Eaqy59k#WBZ@BCVcjB9D&IAHS!`WMD6Y>%0x zrAso|9~!B#elFF$8E=7&)bjZN&IB29Z+?M(U(@_++UUCnu}ddc9m;Qq&6Xc`{d~|z zYxE{E`E>HTm`Z*R$yfjQ5A^x3jotFU{(SI8`ENJUCfj>_N;R31k(c@N%Z*)?`R9!+ z#PD|;X&nR$g(rpTkB~qyGcDLil58E&QRO8{UPO{wWR^&>o8;@1yib*t3D@_c)X592 zMWxOr?4PdW!IW&7HtS?qUPn2sqH5a{GhL)hv-NrBg>4_cTGpA>AWo7+X;dvd2_i;X zo$IuicbXC^ZSVrq3XZp=NAjMEn)$+uD)_^`V_sKLU9xTDM_1Ze5=SZh!VV>K$J*gn z{yDlwMA~Jms#ginD8(OzMJ(O;edJ8E7*!K3oLkdZp^hA5wEq9&uC$`gLP4!)PA!pV zMu@`bw}p&=8WP4Bv9mD@_^3f@pVXAF?n2=w2CTJZC?ir-dSX8?wv2HRN(TxFO6S?6 zIJTg2Oemkitp$b*GqI9_D1465fPwG4(+veYf$M~oQS5$*09&T@*~WKwWWVQnE(rlS zRUNQw7lB7VTEEQlkUC*sF>qQ#ZrTpDKVdz-SH2HGE`^?zG;MR=BC+(=9_Np;AHLTgnxqtqGynh6Grw*9T(TcmrJ-_!!UI9XaCQ^ z4%)sM=ZtH@Z2I!WT|kCVM6RieL9kIjra^DRSGx}b=7xzp(Bw-5XcF^F?aw%#fa{#J zvT5_`yME{cCVjV$NN|spZRC5hbKOO-SMISxCi=HS1pfqSUWV(4HIF=vJ;2rfT|8J} zDuwZTR3t|vM`gYvQZ;Zli5>R?^2%VUc@S8DIfZ`C;mRdR=eHKbj;yMPAZp?yg1g?1@1@jbLb&2Q^M+b zBDwM-)cq&E96~>;jlOpfTTS)2;v=OOrt0Ew0KbB%N5xQk`j8crK^8`2*))79Z%Q-|Uh>aMv(fy+VSdLPb$u#uTW`k#r)S*lY}y#Ws0{hEdXnr0h_TL z!RFh236W>nI7Dc{>HKPpq=KC+iYC%aG4=;^@{!3^f};-j!3#oN?nemceBkxAh45B(xUV!OyWPLX9#L}%kv%sz14%=1Bg`~ z1DpktUOea!ILilk$Umfn_07D}c^U_hAER-!@f&VGcNKuHcWDtOo$U<?L_AG4A92;sYHyMH<2tXrfK&Ww3|j)bSP+X3@OuDmZ6tO1i==^XJ!xRw zpF`W!i2`9WF2?1gepg4@xT|y2X9=tS0zHE#JdF5wFT2+Z3ZfKIR^b+M5P))oDVUsm2B8RtxQhpzeX;u1o zRS@}Y@;~IasLJeO9|>};CJPnxf9{IX8RehQq#pHLnrl$5Z`x!xOmQ;9W7^<1@6*`R zex%I?E~tU(y`>ttkbAq6802)#8LDM#&eoJwfr4UbQ{`tiP|Y_^1WZf3Fw=5gfu%E^ zZFZnZFykaB9$f$x*x7;qS|=|Sp|mDImvZFI!itB-YXg+k*Ft&KJi)K^%K9qm@wVmH z___NR&iq`io(JP1b~8~EWCRizQ-l}3E`CY?ASTbY$w~n#BvT4Dd$klqgPItdx5{`! zVZoGs8ls0+{WMxV@ly81ci{g`9owASvsV?2lI-kG5fncFVOdZqGc|zYudumRQK{@N zk(UoC=}{6Q$$pcHA-pN(E62dO_Mqv-}uP?|hf+vbdI?AKgH-pg3 zY?oUn$s*2P z`+({sCFGe;uWMr*zp%Lp3E+?rD4`)KDI_#$A!%q!`bYzD9N$ZDVr=?dlNRrK zZly{^TOUbpr_pKEvi?exZevQ-MV(Y-nkG?otL_ce?A=uwl{!_C_F!p$O#8KSzH5Wg zx+`(+_np^w&bjCDnSVe12hsJ3(`hrnv$Or>>Db^eU9TINuWr6$^f1n5z+N|y2)syG z{hk`;0Gc1A?QW07s34iHl)94m<8Q}t(`fD;@26?GL=r87h_h^iQMaQ znr-Ca6Jt*t89p?Ag!Cb49{@Omnsh+^*xtf~<-7LRK;83>C^L%c$N=(K$u3;4t0E8L z_J4F7M(iMhApoKo00$4a_!s9JEM|{pOQg@u&BQZ_898I~cl{i2KQu^;$jmbmQ!t)4 z@WywUf=MzxWS9i=3g#TfkUkfT@=&zN$}^HNXOMqkYj(V6x`AiFbD>WDSJ*4N>~8r` zowq0%Z&bv_!NL5{a9T)YIC?Y(M+@A!d5#D(*-NR+dG3-x#8fuJr7~05T^ui@=Uxyv zfluWCDemHgT(XN`C}M0rFd^=o=+IeUSz#KSh~-0+BD^r0$nZju-%M&WXv;2G+1fwqz6BDd_B{I{C3&n_sMRD zW!DDsH1LWDUNn5c2FqUGYGd<4-94*2WLt2pZ8rqkZ;CjhM%^!ejpjMd|F7Le39t$r$%7HD{=h1i-j zt0*K$a$2(rVlpu+&~TB&F_bxn^t$Kf66sWKo;Fmi?pz9N&k1mNDid?nM5lnvjpA%J zmztW7=Tf<}5YJ@E3`xoZ+m5=ksPF;;`nhK1H@EdeEI-+Hn)&n1_SR(+euB2+OpAQ5 zy}5{O?_|uVnJxT*z2R7)B#PC+$as!JNo`Ov`r^wdgxfU5)eO?-X9nQ4T5!$l~2*xO)lgT1}7cfj7rI}1)Q(aF0i0UD$c zy5O!JvGFFX#C#i`XL)zXkYRYX;L?2v@SdtgB%JeBRa6~>dsNlIDjSt%B^S8Kl3vLr zvD69pc3>F&YMG7=w{yOAm9DCW8XQ~2)rN-mQnxKq(Zo0DDs%pIH4v?;Nidpy`Q5{b zbaE~YWyn<$Hj@-Mn(EwCmQ-f;Od^+@hFMK$E5UwvUhfls8q{kLKcnWzWpjyiTyIF4 zy+{}FWOgo-qh04Na*+UnlL(#wP|@@8AnijZ?wSFondhO8(3yW1T9Sdh1R&-hkKp<& z0@|UmM{1_?LXI{{tqxW&mI!$g@eU#sVG4;toCn{?cFLOqLtcSZ%uSn(=B&0DI+e&f zKq4vR^ll==lQ_>bcTL&q7#90qTujZR(g^~6O>0`0Be_&j%vB^kx+LH>PzilkBfNfS zP24Jn)?#e>hEGjRX>NU8qr_3QU|#4?5UqipjNue9(P3t8dh~%EN>+0~Z%VS+-&xcL z^+ry#!%_o%w}=t}fqr9NB^x0J%& zYPfr;<+fk#8CeOBD)oWYP_z`JB z&fT$dSL3A}{pybXTY=AZ94@!Emsj}ldmAUODu1Dp1 zmiFB4Q13go!W~x}kyXU@sa&6O|EO|&a)mns$g;zu_}Z>{Ro^bvv1{STH{e0r)tRL; zw;!)szOz&ecd6m7YlT~hmGBVSZI52{+_7&{+V|bsdZ+z?FN4iWq+977RwKhJ!G{(` z%WQD*@$VJOt!=8CD|^C=&%N35Et}QWHpR`Yq6s^$nN(k=>gZfJ0zo|RFjLw;erNyq zY~bm)h8NjVpi>QWUOT#UcqOp! zv!;G!GO^I`h28)5NSO^U&fj4>%5LAH_lo0+<#SKdTjPt(E55e(yw^_Mh^oGQE1v#a zhwgX|B2Uv5=Vi;6Y~$s=#n$f^mHH_B&~!l+*f$bhUfb@E&O!#kh|J}JQf#RldWR=X=(M9Ifuw(^ROTqLiXEsP?-h2^m7sL$x7=Cz;!*34#9kb+n zj;1m^mz##42!fd7s*R`8ho(q&hI<9Ra<~;vH7MxWZ>J=>4-5SG0d#!xi3W$yNk7t_cQ#;BA%Qv?d+|^9UpakQ4^} z%JgGc^%J}k5Gw+VHZddk5J5Wvym-+_k{GTK;H^SjlrP1e@kT6N*Lv3s@LKBq8eeM< z%AdwQEq-m-V%fUZeV?UexoJ76j6bzJ{c&{70DEO3@io5JrWvm#cH4aWxZLrZ zGwRUEH3Q(3Q)iU;d1X4S%*-lePMMigr!T4T=au>ID)H~Cr;6Vomu?uE6ANh8-25yK zHtH@oAb+&e!}Q9^&NIwbc~9qX3$=Z-KmVy8n91^UY2iWgTlq@oGwhQVCU(z&km~*~ DL}AGM delta 1446 zcmZWpTWlLe6y4d~_*Fj=KTs2%B)R_FI z#53yL`Yx(!3U!ukn)@a7j7#&-U78nuZJQ5lZmpAgv@Yren~yrFT9zXq1`&Snb_#ER zf*-BG@q;G_o`CQuz*n^poP*-rO+#DSyTR6-5XLZshql7Eb|4Y~!tS-6WYh}quN?t? zqvM`C3^@z}5Qk!}+D^E!E=x7(b0+KNIcKEhu)j(CsKqkKm640&4;+`F@|^RRp@+cK zbS|7PW}5pm*>g#iVe8n#2nP^`5Do(T7d`JW$0CI>cWqHQkPkQ*2`!$fe>yDA*D94T<$HvmQzc0l`@c2KC zakP#ii~$%bKpYm7nD@Q`B_8yD78c2lB4+|QN}B@|d*BFJ;Q2rr(l-KLLMr@)U}}%x zA4mQP0L!K6nD>34)D~q1YxdVv64jmr{A$VyR5ruYkK@086bG zYm{a2oI{x5Ymq(@XA;gFqdOl za)rMa+kot;{sPJDM`j0m5=kO`4tHk|uxZ;p7b4^QMmz{2AIA&iL;g>^2_nsbFO_vs z!fBv4<+Dp{3WITEEES%&#O83T)K-S{91LCMool!@$Ji{`D`l~M9!vV#g^SF18_QAF zt5npTgQIv+%Vy9bD=k|#YLtpAl+B=L0o%>a0$BD7tHr8ru8BFc>`S@{{il#zuP41b z+VL!s{l-ee)XQs&rfyd0V!gp?>>QU9$2xTs)DXlwJjLe|!%+8Ii3RdS>(|6B1$&-) zJSIUsMgVx1q?;GMy7=kETc>`Uy*>NW=;=%FvEcD^$bm&d6F2xsI(k@4h-l@@V79zQ zsllkW$f&VeH4TQhV!^hj!OR_=SNYrNJ&^Qfx*-21ab@52^^?gk>%)K;LO;Sjga;AC z4B_Oo$3;Bs19-%dvJ|~;wp$lRn+L&;fMdfHgf0XF;TZ%k!Un=+goGqTx4(vX9^3{j z&=}-*56$yqkN)8PL+b7rXn!>a`SZhj$pQZU@OcvFYBt|5f7Y7K3*N=Mms RUe3<@Y6=SBh*oj$KB!B&cEIY*sr%YN>sY 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 P!+j8 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 str: + """Clean album name by removing version info, deluxe editions, etc.""" + if not album_name: + return "" + + cleaned = album_name + + # Common album suffixes to remove + album_patterns = [ + r'\s*\(deluxe\s*edition?\)', + r'\s*\(expanded\s*edition?\)', + r'\s*\(remastered?\)', + r'\s*\(remaster\)', + r'\s*\(anniversary\s*edition?\)', + r'\s*\(special\s*edition?\)', + r'\s*\(bonus\s*track\s*version\)', + r'\s*\(.*version\)', # Covers "Taylor's Version", "Radio Version", etc. + r'\s*\[deluxe\]', + r'\s*\[remastered?\]', + r'\s*\[.*version\]', + r'\s*-\s*deluxe', + r'\s*-\s*remastered?', + r'\s*\d{4}\s*remaster', # Year remaster + r'\s*\(\d{4}\s*remaster\)' + ] + + for pattern in album_patterns: + cleaned = re.sub(pattern, '', cleaned, flags=re.IGNORECASE).strip() + + return self.normalize_string(cleaned) + def similarity_score(self, str1: str, str2: str) -> float: """Calculates similarity score between two strings.""" if not str1 or not str2: @@ -259,4 +290,77 @@ class MusicMatchingEngine: # A threshold of 0.6 means the title and artist had to have some reasonable similarity. confident_results = [r for r in sorted_results if r.confidence > 0.6] - return confident_results \ No newline at end of file + return confident_results + + def calculate_album_confidence(self, spotify_album, plex_album_info: Dict[str, Any]) -> float: + """Calculate confidence score for album matching""" + if not spotify_album or not plex_album_info: + return 0.0 + + score = 0.0 + + # 1. Album name similarity (40% weight) + spotify_album_clean = self.clean_album_name(spotify_album.name) + plex_album_clean = self.clean_album_name(plex_album_info['title']) + + name_similarity = self.similarity_score(spotify_album_clean, plex_album_clean) + score += name_similarity * 0.4 + + # 2. Artist similarity (40% weight) + if spotify_album.artists and plex_album_info.get('artist'): + spotify_artist_clean = self.clean_artist(spotify_album.artists[0]) + plex_artist_clean = self.clean_artist(plex_album_info['artist']) + + artist_similarity = self.similarity_score(spotify_artist_clean, plex_artist_clean) + score += artist_similarity * 0.4 + + # 3. Track count similarity (10% weight) + spotify_track_count = getattr(spotify_album, 'total_tracks', 0) + plex_track_count = plex_album_info.get('track_count', 0) + + if spotify_track_count > 0 and plex_track_count > 0: + # Calculate track count similarity (perfect match = 1.0, close matches get partial credit) + track_diff = abs(spotify_track_count - plex_track_count) + if track_diff == 0: + track_similarity = 1.0 + elif track_diff <= 2: # Allow for slight differences (bonus tracks, etc.) + track_similarity = 0.8 + elif track_diff <= 5: + track_similarity = 0.5 + else: + track_similarity = 0.2 + + score += track_similarity * 0.1 + + # 4. Year similarity bonus (10% weight) + spotify_year = spotify_album.release_date[:4] if spotify_album.release_date else None + plex_year = str(plex_album_info.get('year', '')) if plex_album_info.get('year') else None + + if spotify_year and plex_year: + if spotify_year == plex_year: + score += 0.1 # Perfect year match + elif abs(int(spotify_year) - int(plex_year)) <= 1: + score += 0.05 # Close year match (remaster, etc.) + + return min(score, 1.0) # Cap at 1.0 + + def find_best_album_match(self, spotify_album, plex_albums: List[Dict[str, Any]]) -> Tuple[Optional[Dict[str, Any]], float]: + """Find the best matching album from Plex candidates""" + if not plex_albums: + return None, 0.0 + + best_match = None + best_confidence = 0.0 + + for plex_album in plex_albums: + confidence = self.calculate_album_confidence(spotify_album, plex_album) + + if confidence > best_confidence: + best_confidence = confidence + best_match = plex_album + + # Only return matches above confidence threshold + if best_confidence >= 0.8: # High threshold for album matching + return best_match, best_confidence + else: + return None, best_confidence \ No newline at end of file diff --git a/core/plex_client.py b/core/plex_client.py index 2a1dddc2..318924b4 100644 --- a/core/plex_client.py +++ b/core/plex_client.py @@ -441,3 +441,104 @@ class PlexClient: except Exception as e: logger.error(f"Error updating track metadata: {e}") return False + + def search_albums(self, album_name: str = "", artist_name: str = "", limit: int = 20) -> List[Dict[str, Any]]: + """Search for albums in Plex library""" + if not self.ensure_connection() or not self.music_library: + return [] + + try: + albums = [] + + # Perform search - different approaches based on what we're searching for + search_results = [] + + if album_name and artist_name: + # Search for albums by specific artist and title + try: + # First try searching for the artist, then filter their albums + artist_results = self.music_library.searchArtists(title=artist_name, limit=3) + for artist in artist_results: + try: + artist_albums = artist.albums() + for album in artist_albums: + if album_name.lower() in album.title.lower(): + search_results.append(album) + except Exception as e: + logger.debug(f"Error getting albums for artist {artist.title}: {e}") + except Exception as e: + logger.debug(f"Artist search failed, trying general search: {e}") + # Fallback to general album search + try: + search_results = self.music_library.search(title=album_name) + # Filter to only albums + search_results = [r for r in search_results if isinstance(r, PlexAlbum)] + except Exception as e2: + logger.debug(f"General search also failed: {e2}") + + elif album_name: + # Search for albums by title only + try: + search_results = self.music_library.search(title=album_name) + # Filter to only albums + search_results = [r for r in search_results if isinstance(r, PlexAlbum)] + except Exception as e: + logger.debug(f"Album title search failed: {e}") + + elif artist_name: + # Search for all albums by artist + try: + artist_results = self.music_library.searchArtists(title=artist_name, limit=1) + if artist_results: + search_results = artist_results[0].albums() + except Exception as e: + logger.debug(f"Artist album search failed: {e}") + else: + # Get all albums if no search terms + try: + search_results = self.music_library.albums() + except Exception as e: + logger.debug(f"Get all albums failed: {e}") + + # Process results and convert to standardized format + if search_results: + for result in search_results: + if isinstance(result, PlexAlbum): + try: + # Get album info + album_info = { + 'id': str(result.ratingKey), + 'title': result.title, + 'artist': result.artist().title if result.artist() else "Unknown Artist", + 'year': result.year, + 'track_count': len(result.tracks()) if hasattr(result, 'tracks') else 0, + 'plex_album': result # Keep reference to original object + } + albums.append(album_info) + + if len(albums) >= limit: + break + + except Exception as e: + logger.debug(f"Error processing album {result.title}: {e}") + continue + + logger.debug(f"Found {len(albums)} albums matching query: album='{album_name}', artist='{artist_name}'") + return albums + + except Exception as e: + logger.error(f"Error searching albums: {e}") + return [] + + def get_album_by_name_and_artist(self, album_name: str, artist_name: str) -> Optional[Dict[str, Any]]: + """Get a specific album by name and artist""" + albums = self.search_albums(album_name, artist_name, limit=5) + + # Look for exact matches first + for album in albums: + if (album['title'].lower() == album_name.lower() and + album['artist'].lower() == artist_name.lower()): + return album + + # Return first result if no exact match + return albums[0] if albums else None diff --git a/logs/app.log b/logs/app.log index 46061f11..532b2dee 100644 --- a/logs/app.log +++ b/logs/app.log @@ -45333,3 +45333,209 @@ 2025-07-25 17:51:45 - newmusic.main - INFO - closeEvent:186 - Stopping status monitoring thread... 2025-07-25 17:51:46 - newmusic.main - INFO - closeEvent:191 - Closing Soulseek client... 2025-07-25 17:51:46 - newmusic.main - INFO - closeEvent:197 - Application closed successfully +2025-07-25 17:56:28 - newmusic - INFO - setup_logging:57 - Logging initialized with level: DEBUG +2025-07-25 17:56:28 - newmusic.main - INFO - main:211 - Starting NewMusic application +2025-07-25 17:56:28 - newmusic.spotify_client - INFO - _setup_client:179 - Spotify client initialized (user info will be fetched when needed) +2025-07-25 17:56:28 - newmusic.soulseek_client - INFO - _setup_client:220 - Soulseek client configured with slskd at http://localhost:5030 +2025-07-25 17:56:28 - newmusic.spotify_client - INFO - _setup_client:179 - Spotify client initialized (user info will be fetched when needed) +2025-07-25 17:56:29 - newmusic.spotify_client - INFO - _setup_client:179 - Spotify client initialized (user info will be fetched when needed) +2025-07-25 17:56:29 - newmusic.soulseek_client - INFO - _setup_client:220 - Soulseek client configured with slskd at http://localhost:5030 +2025-07-25 17:56:29 - newmusic.main - INFO - change_page:163 - Changed to page: dashboard +2025-07-25 17:56:29 - newmusic.main - INFO - setup_media_player_connections:150 - Media player connections established between sidebar and downloads page +2025-07-25 17:56:29 - newmusic.plex_client - INFO - _find_music_library:127 - Found music library: Music +2025-07-25 17:56:29 - newmusic.plex_client - INFO - _setup_client:113 - Successfully connected to Plex server: PLEX-MACHINE +2025-07-25 17:56:58 - newmusic.main - INFO - change_page:163 - Changed to page: artists +2025-07-25 17:57:04 - newmusic.spotify_client - INFO - get_artist_albums:452 - Retrieved 30 albums for artist 06HL4z0CvFAxyc27GXpf02 +2025-07-25 17:57:04 - newmusic.plex_client - INFO - _find_music_library:127 - Found music library: Music +2025-07-25 17:57:04 - newmusic.plex_client - INFO - _setup_client:113 - Successfully connected to Plex server: PLEX-MACHINE +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:57:04 - newmusic.plex_client - ERROR - search_albums:493 - Error searching albums: Unknown filter field "artist" for libtype "artist". Available filter fields: ['artist.title', 'artist.userRating', 'artist.genre', 'artist.collection', 'artist.country', 'artist.mood', 'artist.style', 'artist.addedAt', 'artist.lastViewedAt', 'artist.unmatched', 'artist.guid', 'artist.id', 'artist.index', 'artist.lastRatedAt', 'artist.updatedAt', 'group', 'having', 'artist.label'] +2025-07-25 17:59:05 - newmusic.main - INFO - closeEvent:176 - Closing application... +2025-07-25 17:59:05 - newmusic.main - INFO - closeEvent:181 - Cleaning up Downloads page threads... +2025-07-25 17:59:05 - newmusic.main - INFO - closeEvent:186 - Stopping status monitoring thread... +2025-07-25 17:59:06 - newmusic.main - INFO - closeEvent:191 - Closing Soulseek client... +2025-07-25 17:59:06 - newmusic.main - INFO - closeEvent:197 - Application closed successfully +2025-07-25 17:59:44 - newmusic - INFO - setup_logging:57 - Logging initialized with level: DEBUG +2025-07-25 17:59:44 - newmusic.main - INFO - main:211 - Starting NewMusic application +2025-07-25 17:59:44 - newmusic.spotify_client - INFO - _setup_client:179 - Spotify client initialized (user info will be fetched when needed) +2025-07-25 17:59:44 - newmusic.soulseek_client - INFO - _setup_client:220 - Soulseek client configured with slskd at http://localhost:5030 +2025-07-25 17:59:44 - newmusic.spotify_client - INFO - _setup_client:179 - Spotify client initialized (user info will be fetched when needed) +2025-07-25 17:59:44 - newmusic.spotify_client - INFO - _setup_client:179 - Spotify client initialized (user info will be fetched when needed) +2025-07-25 17:59:44 - newmusic.soulseek_client - INFO - _setup_client:220 - Soulseek client configured with slskd at http://localhost:5030 +2025-07-25 17:59:45 - newmusic.main - INFO - change_page:163 - Changed to page: dashboard +2025-07-25 17:59:45 - newmusic.main - INFO - setup_media_player_connections:150 - Media player connections established between sidebar and downloads page +2025-07-25 17:59:45 - newmusic.plex_client - INFO - _find_music_library:127 - Found music library: Music +2025-07-25 17:59:45 - newmusic.plex_client - INFO - _setup_client:113 - Successfully connected to Plex server: PLEX-MACHINE +2025-07-25 17:59:51 - newmusic.main - INFO - change_page:163 - Changed to page: artists +2025-07-25 18:00:19 - newmusic.spotify_client - INFO - get_artist_albums:452 - Retrieved 30 albums for artist 06HL4z0CvFAxyc27GXpf02 +2025-07-25 18:00:19 - newmusic.plex_client - INFO - _find_music_library:127 - Found music library: Music +2025-07-25 18:00:19 - newmusic.plex_client - INFO - _setup_client:113 - Successfully connected to Plex server: PLEX-MACHINE +2025-07-25 18:00:20 - newmusic.plex_client - DEBUG - search_albums:526 - Found 1 albums matching query: album='THE TORTURED POETS DEPARTMENT: THE ANTHOLOGY', artist='taylor swift' +2025-07-25 18:00:20 - newmusic.plex_client - DEBUG - search_albums:526 - Found 0 albums matching query: album='the tortured poets department the anthology', artist='taylor swift' +2025-07-25 18:00:20 - newmusic.plex_client - DEBUG - search_albums:526 - Found 0 albums matching query: album='the tortured poets department the anthology', artist='' +2025-07-25 18:00:20 - newmusic.plex_client - DEBUG - search_albums:526 - Found 2 albums matching query: album='THE TORTURED POETS DEPARTMENT', artist='taylor swift' +2025-07-25 18:00:20 - newmusic.plex_client - DEBUG - search_albums:526 - Found 0 albums matching query: album='1989 (Taylor's Version) [Deluxe]', artist='taylor swift' +2025-07-25 18:00:20 - newmusic.plex_client - DEBUG - search_albums:526 - Found 0 albums matching query: album='1989 (Taylor's Version) [Deluxe]', artist='' +2025-07-25 18:00:20 - newmusic.plex_client - DEBUG - search_albums:526 - Found 5 albums matching query: album='1989', artist='taylor swift' +2025-07-25 18:00:20 - newmusic.plex_client - DEBUG - search_albums:526 - Found 2 albums matching query: album='1989 (Taylor's Version)', artist='taylor swift' +2025-07-25 18:00:21 - newmusic.plex_client - DEBUG - search_albums:526 - Found 5 albums matching query: album='1989', artist='taylor swift' +2025-07-25 18:00:21 - newmusic.plex_client - DEBUG - search_albums:526 - Found 1 albums matching query: album='Speak Now (Taylor's Version)', artist='taylor swift' +2025-07-25 18:00:21 - newmusic.plex_client - DEBUG - search_albums:526 - Found 5 albums matching query: album='speak now', artist='taylor swift' +2025-07-25 18:00:21 - newmusic.plex_client - DEBUG - search_albums:526 - Found 1 albums matching query: album='Midnights (The Til Dawn Edition)', artist='taylor swift' +2025-07-25 18:00:21 - newmusic.plex_client - DEBUG - search_albums:526 - Found 0 albums matching query: album='midnights the til dawn edition', artist='taylor swift' +2025-07-25 18:00:21 - newmusic.plex_client - DEBUG - search_albums:526 - Found 0 albums matching query: album='midnights the til dawn edition', artist='' +2025-07-25 18:00:21 - newmusic.plex_client - DEBUG - search_albums:526 - Found 1 albums matching query: album='Midnights (3am Edition)', artist='taylor swift' +2025-07-25 18:00:21 - newmusic.plex_client - DEBUG - search_albums:526 - Found 0 albums matching query: album='midnights 3am edition', artist='taylor swift' +2025-07-25 18:00:21 - newmusic.plex_client - DEBUG - search_albums:526 - Found 0 albums matching query: album='midnights 3am edition', artist='' +2025-07-25 18:00:22 - newmusic.plex_client - DEBUG - search_albums:526 - Found 3 albums matching query: album='Midnights', artist='taylor swift' +2025-07-25 18:00:22 - newmusic.plex_client - DEBUG - search_albums:526 - Found 1 albums matching query: album='Red (Taylor's Version)', artist='taylor swift' +2025-07-25 18:00:22 - newmusic.plex_client - DEBUG - search_albums:526 - Found 5 albums matching query: album='red', artist='taylor swift' +2025-07-25 18:00:22 - newmusic.plex_client - DEBUG - search_albums:526 - Found 3 albums matching query: album='Fearless (Taylor's Version)', artist='taylor swift' +2025-07-25 18:00:22 - newmusic.plex_client - DEBUG - search_albums:526 - Found 5 albums matching query: album='fearless', artist='taylor swift' +2025-07-25 18:00:22 - newmusic.plex_client - DEBUG - search_albums:526 - Found 1 albums matching query: album='evermore (deluxe version)', artist='taylor swift' +2025-07-25 18:00:23 - newmusic.plex_client - DEBUG - search_albums:526 - Found 2 albums matching query: album='evermore', artist='taylor swift' +2025-07-25 18:00:23 - newmusic.plex_client - DEBUG - search_albums:526 - Found 2 albums matching query: album='evermore', artist='taylor swift' +2025-07-25 18:00:23 - newmusic.plex_client - DEBUG - search_albums:526 - Found 0 albums matching query: album='folklore: the long pond studio sessions (from the Disney+ special) [deluxe edition]', artist='taylor swift' +2025-07-25 18:00:23 - newmusic.plex_client - DEBUG - search_albums:526 - Found 0 albums matching query: album='folklore: the long pond studio sessions (from the Disney+ special) [deluxe edition]', artist='' +2025-07-25 18:00:23 - newmusic.plex_client - DEBUG - search_albums:526 - Found 0 albums matching query: album='folklore the long pond studio sessions from the disney special deluxe edition', artist='taylor swift' +2025-07-25 18:00:23 - newmusic.plex_client - DEBUG - search_albums:526 - Found 0 albums matching query: album='folklore the long pond studio sessions from the disney special deluxe edition', artist='' +2025-07-25 18:00:23 - newmusic.plex_client - DEBUG - search_albums:526 - Found 1 albums matching query: album='folklore (deluxe version)', artist='taylor swift' +2025-07-25 18:00:23 - newmusic.plex_client - DEBUG - search_albums:526 - Found 5 albums matching query: album='folklore', artist='taylor swift' +2025-07-25 18:00:23 - newmusic.plex_client - DEBUG - search_albums:526 - Found 5 albums matching query: album='folklore', artist='taylor swift' +2025-07-25 18:00:24 - newmusic.plex_client - DEBUG - search_albums:526 - Found 2 albums matching query: album='Lover', artist='taylor swift' +2025-07-25 18:00:24 - newmusic.plex_client - DEBUG - search_albums:526 - Found 1 albums matching query: album='Taylor Swift Karaoke: reputation', artist='taylor swift' +2025-07-25 18:00:24 - newmusic.plex_client - DEBUG - search_albums:526 - Found 0 albums matching query: album='taylor swift karaoke reputation', artist='taylor swift' +2025-07-25 18:00:24 - newmusic.plex_client - DEBUG - search_albums:526 - Found 0 albums matching query: album='taylor swift karaoke reputation', artist='' +2025-07-25 18:00:24 - newmusic.plex_client - DEBUG - search_albums:526 - Found 4 albums matching query: album='reputation', artist='taylor swift' +2025-07-25 18:00:24 - newmusic.plex_client - DEBUG - search_albums:526 - Found 1 albums matching query: album='reputation Stadium Tour Surprise Song Playlist', artist='taylor swift' +2025-07-25 18:00:24 - newmusic.plex_client - DEBUG - search_albums:526 - Found 1 albums matching query: album='1989 (Deluxe Edition)', artist='taylor swift' +2025-07-25 18:00:24 - newmusic.plex_client - DEBUG - search_albums:526 - Found 5 albums matching query: album='1989', artist='taylor swift' +2025-07-25 18:00:25 - newmusic.plex_client - DEBUG - search_albums:526 - Found 5 albums matching query: album='1989', artist='taylor swift' +2025-07-25 18:00:25 - newmusic.plex_client - DEBUG - search_albums:526 - Found 1 albums matching query: album='Red (Deluxe Edition)', artist='taylor swift' +2025-07-25 18:00:25 - newmusic.plex_client - DEBUG - search_albums:526 - Found 5 albums matching query: album='red', artist='taylor swift' +2025-07-25 18:00:25 - newmusic.plex_client - DEBUG - search_albums:526 - Found 5 albums matching query: album='Red', artist='taylor swift' +2025-07-25 18:00:25 - newmusic.plex_client - DEBUG - search_albums:526 - Found 1 albums matching query: album='Speak Now World Tour Live', artist='taylor swift' +2025-07-25 18:00:26 - newmusic.plex_client - DEBUG - search_albums:526 - Found 1 albums matching query: album='Speak Now (Deluxe Edition)', artist='taylor swift' +2025-07-25 18:00:26 - newmusic.plex_client - DEBUG - search_albums:526 - Found 5 albums matching query: album='speak now', artist='taylor swift' +2025-07-25 18:00:26 - newmusic.plex_client - DEBUG - search_albums:526 - Found 5 albums matching query: album='Speak Now', artist='taylor swift' +2025-07-25 18:00:26 - newmusic.plex_client - DEBUG - search_albums:526 - Found 0 albums matching query: album='Fearless Platinum Edition', artist='taylor swift' +2025-07-25 18:00:26 - newmusic.plex_client - DEBUG - search_albums:526 - Found 0 albums matching query: album='Fearless Platinum Edition', artist='' +2025-07-25 18:00:26 - newmusic.plex_client - DEBUG - search_albums:526 - Found 5 albums matching query: album='Fearless', artist='taylor swift' +2025-07-25 18:00:27 - newmusic.plex_client - DEBUG - search_albums:526 - Found 1 albums matching query: album='Live From Clear Channel Stripped 2008', artist='taylor swift' +2025-07-25 18:00:27 - newmusic.plex_client - DEBUG - search_albums:526 - Found 5 albums matching query: album='Taylor Swift', artist='taylor swift' +2025-07-25 18:02:16 - newmusic.main - INFO - closeEvent:176 - Closing application... +2025-07-25 18:02:16 - newmusic.main - INFO - closeEvent:181 - Cleaning up Downloads page threads... +2025-07-25 18:02:16 - newmusic.main - INFO - closeEvent:186 - Stopping status monitoring thread... +2025-07-25 18:02:18 - newmusic.main - INFO - closeEvent:191 - Closing Soulseek client... +2025-07-25 18:02:18 - newmusic.main - INFO - closeEvent:197 - Application closed successfully +2025-07-25 18:02:26 - newmusic - INFO - setup_logging:57 - Logging initialized with level: DEBUG +2025-07-25 18:02:26 - newmusic.main - INFO - main:211 - Starting NewMusic application +2025-07-25 18:02:26 - newmusic.spotify_client - INFO - _setup_client:179 - Spotify client initialized (user info will be fetched when needed) +2025-07-25 18:02:26 - newmusic.soulseek_client - INFO - _setup_client:220 - Soulseek client configured with slskd at http://localhost:5030 +2025-07-25 18:02:26 - newmusic.spotify_client - INFO - _setup_client:179 - Spotify client initialized (user info will be fetched when needed) +2025-07-25 18:02:26 - newmusic.spotify_client - INFO - _setup_client:179 - Spotify client initialized (user info will be fetched when needed) +2025-07-25 18:02:26 - newmusic.soulseek_client - INFO - _setup_client:220 - Soulseek client configured with slskd at http://localhost:5030 +2025-07-25 18:02:26 - newmusic.main - INFO - change_page:163 - Changed to page: dashboard +2025-07-25 18:02:26 - newmusic.main - INFO - setup_media_player_connections:150 - Media player connections established between sidebar and downloads page +2025-07-25 18:02:27 - newmusic.plex_client - INFO - _find_music_library:127 - Found music library: Music +2025-07-25 18:02:27 - newmusic.plex_client - INFO - _setup_client:113 - Successfully connected to Plex server: PLEX-MACHINE +2025-07-25 18:02:39 - newmusic.main - INFO - change_page:163 - Changed to page: artists +2025-07-25 18:02:48 - newmusic.spotify_client - INFO - get_artist_albums:452 - Retrieved 30 albums for artist 06HL4z0CvFAxyc27GXpf02 +2025-07-25 18:02:48 - newmusic.plex_client - INFO - _find_music_library:127 - Found music library: Music +2025-07-25 18:02:48 - newmusic.plex_client - INFO - _setup_client:113 - Successfully connected to Plex server: PLEX-MACHINE +2025-07-25 18:02:48 - newmusic.plex_client - DEBUG - search_albums:526 - Found 1 albums matching query: album='THE TORTURED POETS DEPARTMENT: THE ANTHOLOGY', artist='taylor swift' +2025-07-25 18:02:48 - newmusic.plex_client - DEBUG - search_albums:526 - Found 0 albums matching query: album='the tortured poets department the anthology', artist='taylor swift' +2025-07-25 18:02:48 - newmusic.plex_client - DEBUG - search_albums:526 - Found 0 albums matching query: album='the tortured poets department the anthology', artist='' +2025-07-25 18:02:48 - newmusic.plex_client - DEBUG - search_albums:526 - Found 2 albums matching query: album='THE TORTURED POETS DEPARTMENT', artist='taylor swift' +2025-07-25 18:02:48 - newmusic.plex_client - DEBUG - search_albums:526 - Found 0 albums matching query: album='1989 (Taylor's Version) [Deluxe]', artist='taylor swift' +2025-07-25 18:02:48 - newmusic.plex_client - DEBUG - search_albums:526 - Found 0 albums matching query: album='1989 (Taylor's Version) [Deluxe]', artist='' +2025-07-25 18:02:48 - newmusic.plex_client - DEBUG - search_albums:526 - Found 5 albums matching query: album='1989', artist='taylor swift' +2025-07-25 18:02:49 - newmusic.plex_client - DEBUG - search_albums:526 - Found 2 albums matching query: album='1989 (Taylor's Version)', artist='taylor swift' +2025-07-25 18:02:49 - newmusic.plex_client - DEBUG - search_albums:526 - Found 5 albums matching query: album='1989', artist='taylor swift' +2025-07-25 18:02:49 - newmusic.plex_client - DEBUG - search_albums:526 - Found 1 albums matching query: album='Speak Now (Taylor's Version)', artist='taylor swift' +2025-07-25 18:02:49 - newmusic.plex_client - DEBUG - search_albums:526 - Found 5 albums matching query: album='speak now', artist='taylor swift' +2025-07-25 18:02:49 - newmusic.plex_client - DEBUG - search_albums:526 - Found 1 albums matching query: album='Midnights (The Til Dawn Edition)', artist='taylor swift' +2025-07-25 18:02:49 - newmusic.plex_client - DEBUG - search_albums:526 - Found 0 albums matching query: album='midnights the til dawn edition', artist='taylor swift' +2025-07-25 18:02:49 - newmusic.plex_client - DEBUG - search_albums:526 - Found 0 albums matching query: album='midnights the til dawn edition', artist='' +2025-07-25 18:02:49 - newmusic.plex_client - DEBUG - search_albums:526 - Found 1 albums matching query: album='Midnights (3am Edition)', artist='taylor swift' +2025-07-25 18:02:49 - newmusic.plex_client - DEBUG - search_albums:526 - Found 0 albums matching query: album='midnights 3am edition', artist='taylor swift' +2025-07-25 18:02:49 - newmusic.plex_client - DEBUG - search_albums:526 - Found 0 albums matching query: album='midnights 3am edition', artist='' +2025-07-25 18:02:50 - newmusic.plex_client - DEBUG - search_albums:526 - Found 3 albums matching query: album='Midnights', artist='taylor swift' +2025-07-25 18:02:50 - newmusic.plex_client - DEBUG - search_albums:526 - Found 1 albums matching query: album='Red (Taylor's Version)', artist='taylor swift' +2025-07-25 18:02:50 - newmusic.plex_client - DEBUG - search_albums:526 - Found 5 albums matching query: album='red', artist='taylor swift' +2025-07-25 18:02:50 - newmusic.plex_client - DEBUG - search_albums:526 - Found 3 albums matching query: album='Fearless (Taylor's Version)', artist='taylor swift' +2025-07-25 18:02:50 - newmusic.plex_client - DEBUG - search_albums:526 - Found 5 albums matching query: album='fearless', artist='taylor swift' +2025-07-25 18:02:51 - newmusic.plex_client - DEBUG - search_albums:526 - Found 1 albums matching query: album='evermore (deluxe version)', artist='taylor swift' +2025-07-25 18:02:51 - newmusic.plex_client - DEBUG - search_albums:526 - Found 2 albums matching query: album='evermore', artist='taylor swift' +2025-07-25 18:02:51 - newmusic.plex_client - DEBUG - search_albums:526 - Found 2 albums matching query: album='evermore', artist='taylor swift' +2025-07-25 18:02:51 - newmusic.plex_client - DEBUG - search_albums:526 - Found 0 albums matching query: album='folklore: the long pond studio sessions (from the Disney+ special) [deluxe edition]', artist='taylor swift' +2025-07-25 18:02:51 - newmusic.plex_client - DEBUG - search_albums:526 - Found 0 albums matching query: album='folklore: the long pond studio sessions (from the Disney+ special) [deluxe edition]', artist='' +2025-07-25 18:02:51 - newmusic.plex_client - DEBUG - search_albums:526 - Found 0 albums matching query: album='folklore the long pond studio sessions from the disney special deluxe edition', artist='taylor swift' +2025-07-25 18:02:51 - newmusic.plex_client - DEBUG - search_albums:526 - Found 0 albums matching query: album='folklore the long pond studio sessions from the disney special deluxe edition', artist='' +2025-07-25 18:02:51 - newmusic.plex_client - DEBUG - search_albums:526 - Found 1 albums matching query: album='folklore (deluxe version)', artist='taylor swift' +2025-07-25 18:02:51 - newmusic.plex_client - DEBUG - search_albums:526 - Found 5 albums matching query: album='folklore', artist='taylor swift' +2025-07-25 18:02:52 - newmusic.plex_client - DEBUG - search_albums:526 - Found 5 albums matching query: album='folklore', artist='taylor swift' +2025-07-25 18:02:52 - newmusic.plex_client - DEBUG - search_albums:526 - Found 2 albums matching query: album='Lover', artist='taylor swift' +2025-07-25 18:02:52 - newmusic.plex_client - DEBUG - search_albums:526 - Found 1 albums matching query: album='Taylor Swift Karaoke: reputation', artist='taylor swift' +2025-07-25 18:02:52 - newmusic.plex_client - DEBUG - search_albums:526 - Found 0 albums matching query: album='taylor swift karaoke reputation', artist='taylor swift' +2025-07-25 18:02:52 - newmusic.plex_client - DEBUG - search_albums:526 - Found 0 albums matching query: album='taylor swift karaoke reputation', artist='' +2025-07-25 18:02:52 - newmusic.plex_client - DEBUG - search_albums:526 - Found 4 albums matching query: album='reputation', artist='taylor swift' +2025-07-25 18:02:52 - newmusic.plex_client - DEBUG - search_albums:526 - Found 1 albums matching query: album='reputation Stadium Tour Surprise Song Playlist', artist='taylor swift' +2025-07-25 18:02:52 - newmusic.plex_client - DEBUG - search_albums:526 - Found 1 albums matching query: album='1989 (Deluxe Edition)', artist='taylor swift' +2025-07-25 18:02:52 - newmusic.plex_client - DEBUG - search_albums:526 - Found 5 albums matching query: album='1989', artist='taylor swift' +2025-07-25 18:02:53 - newmusic.plex_client - DEBUG - search_albums:526 - Found 5 albums matching query: album='1989', artist='taylor swift' +2025-07-25 18:02:53 - newmusic.plex_client - DEBUG - search_albums:526 - Found 1 albums matching query: album='Red (Deluxe Edition)', artist='taylor swift' +2025-07-25 18:02:53 - newmusic.plex_client - DEBUG - search_albums:526 - Found 5 albums matching query: album='red', artist='taylor swift' +2025-07-25 18:02:53 - newmusic.plex_client - DEBUG - search_albums:526 - Found 5 albums matching query: album='Red', artist='taylor swift' +2025-07-25 18:02:53 - newmusic.plex_client - DEBUG - search_albums:526 - Found 1 albums matching query: album='Speak Now World Tour Live', artist='taylor swift' +2025-07-25 18:02:53 - newmusic.plex_client - DEBUG - search_albums:526 - Found 1 albums matching query: album='Speak Now (Deluxe Edition)', artist='taylor swift' +2025-07-25 18:02:54 - newmusic.plex_client - DEBUG - search_albums:526 - Found 5 albums matching query: album='speak now', artist='taylor swift' +2025-07-25 18:02:54 - newmusic.plex_client - DEBUG - search_albums:526 - Found 5 albums matching query: album='Speak Now', artist='taylor swift' +2025-07-25 18:02:54 - newmusic.plex_client - DEBUG - search_albums:526 - Found 0 albums matching query: album='Fearless Platinum Edition', artist='taylor swift' +2025-07-25 18:02:54 - newmusic.plex_client - DEBUG - search_albums:526 - Found 0 albums matching query: album='Fearless Platinum Edition', artist='' +2025-07-25 18:02:54 - newmusic.plex_client - DEBUG - search_albums:526 - Found 5 albums matching query: album='Fearless', artist='taylor swift' +2025-07-25 18:02:54 - newmusic.plex_client - DEBUG - search_albums:526 - Found 1 albums matching query: album='Live From Clear Channel Stripped 2008', artist='taylor swift' +2025-07-25 18:02:55 - newmusic.plex_client - DEBUG - search_albums:526 - Found 5 albums matching query: album='Taylor Swift', artist='taylor swift' diff --git a/ui/pages/__pycache__/artists.cpython-310.pyc b/ui/pages/__pycache__/artists.cpython-310.pyc index d2ebe48a4a8abbd01d53d9ea81ae7f4474f72016..9ef393beaeef5832ca319262582d85bef79b49e2 100644 GIT binary patch delta 8242 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 RTN0LpcRBoKS`xX1{|~@6Y~cU^ delta 6449 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 E54t*kRR910 diff --git a/ui/pages/__pycache__/artists.cpython-312.pyc b/ui/pages/__pycache__/artists.cpython-312.pyc index c61e7038e1d4893a62bea7ba6cb6956aeeec925d..b793437dd7737e823016670186661c9a4b62103a 100644 GIT binary patch delta 10291 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 BCIbKf diff --git a/ui/pages/artists.py b/ui/pages/artists.py index 41e513c9..e311d398 100644 --- a/ui/pages/artists.py +++ b/ui/pages/artists.py @@ -208,7 +208,7 @@ class PlexLibraryWorker(QThread): def run(self): try: - print("🔍 Starting Plex library check...") + print("🔍 Starting robust Plex album matching...") owned_albums = set() if not self.plex_client or not self.plex_client.ensure_connection(): @@ -219,36 +219,77 @@ class PlexLibraryWorker(QThread): if self._stop_requested: return - # Get a smaller sample of tracks to avoid loading 10,000 items - print("📚 Searching Plex library for albums...") - plex_tracks = self.plex_client.search_tracks("", "", limit=1000) # Reduced from 10000 + print(f"📚 Checking {len(self.albums)} Spotify albums against Plex library...") - if self._stop_requested: - return - - # Extract unique album names from Plex - plex_album_names = set() - for track in plex_tracks: + # Use robust matching for each album + for i, spotify_album in enumerate(self.albums): if self._stop_requested: return + + print(f"🎵 Checking album {i+1}/{len(self.albums)}: {spotify_album.name}") + + # Create multiple search variations + album_variations = [] + + # Original name + album_variations.append(spotify_album.name) + + # Cleaned name (removes versions, etc.) + cleaned_name = self.matching_engine.clean_album_name(spotify_album.name) + if cleaned_name != spotify_album.name.lower(): + album_variations.append(cleaned_name) + + # Try different artist combinations + artists_to_try = spotify_album.artists[:2] if spotify_album.artists else [""] + + all_plex_matches = [] + + # Search with different combinations + for artist in artists_to_try: + if self._stop_requested: + return - if track.album and track.album != "Unknown Album": - plex_album_names.add(track.album.lower()) - - print(f"📀 Found {len(plex_album_names)} unique albums in Plex library") - - # Check each Spotify album against Plex albums - for album in self.albums: - if self._stop_requested: - return + artist_clean = self.matching_engine.clean_artist(artist) if artist else "" - album_name_lower = album.name.lower() - for plex_album in plex_album_names: - if self.matching_engine.similarity_score(album_name_lower, plex_album) > 0.8: - owned_albums.add(album.name) - break + for album_name in album_variations: + if self._stop_requested: + return + + # Search Plex for this combination + print(f" 🔍 Searching Plex: album='{album_name}', artist='{artist_clean}'") + plex_albums = self.plex_client.search_albums(album_name, artist_clean, limit=5) + print(f" 📀 Found {len(plex_albums)} Plex albums") + all_plex_matches.extend(plex_albums) + + # Also try album-only search if artist+album didn't work + if not plex_albums and artist_clean: + print(f" 🔍 Trying album-only search: album='{album_name}'") + album_only_results = self.plex_client.search_albums(album_name, "", limit=5) + print(f" 📀 Found {len(album_only_results)} albums (album-only)") + all_plex_matches.extend(album_only_results) + + # Remove duplicates based on album ID + unique_matches = {} + for match in all_plex_matches: + unique_matches[match['id']] = match + + unique_plex_albums = list(unique_matches.values()) + + if unique_plex_albums: + # Use robust matching to find best match + best_match, confidence = self.matching_engine.find_best_album_match( + spotify_album, unique_plex_albums + ) + + if best_match and confidence >= 0.8: + owned_albums.add(spotify_album.name) + print(f"✅ Match found: '{spotify_album.name}' -> '{best_match['title']}' (confidence: {confidence:.2f})") + else: + print(f"❌ No confident match for '{spotify_album.name}' (best: {confidence:.2f})") + else: + print(f"❌ No Plex candidates found for '{spotify_album.name}'") - print(f"✅ Found {len(owned_albums)} owned albums") + print(f"🎯 Final result: {len(owned_albums)} owned albums out of {len(self.albums)}") self.library_checked.emit(owned_albums) except Exception as e: @@ -743,6 +784,7 @@ class AlbumCard(QFrame): super().__init__(parent) self.album = album self.is_owned = is_owned + print(f"🎨 AlbumCard created: '{album.name}' - is_owned: {is_owned}") self.setup_ui() self.load_album_image() @@ -803,7 +845,10 @@ class AlbumCard(QFrame): self.overlay.setFixedSize(164, 164) self.overlay.setAlignment(Qt.AlignmentFlag.AlignCenter) + print(f"🎨 Setting up overlay for '{self.album.name}' - is_owned: {self.is_owned}") + if self.is_owned: + print(f"🎨 Creating OWNED overlay for '{self.album.name}'") self.overlay.setStyleSheet(""" QLabel { background: rgba(29, 185, 84, 0.8); @@ -815,6 +860,7 @@ class AlbumCard(QFrame): """) self.overlay.setText("✓") else: + print(f"🎨 Creating DOWNLOAD overlay for '{self.album.name}'") self.overlay.setStyleSheet(""" QLabel { background: rgba(0, 0, 0, 0.7); @@ -1359,6 +1405,8 @@ class ArtistsPage(QWidget): def display_albums(self, albums, owned_albums): """Display albums in the grid""" + print(f"🎨 Refreshing UI with {len(albums)} albums, {len(owned_albums)} owned") + # Clear existing albums self.clear_albums() @@ -1367,6 +1415,8 @@ class ArtistsPage(QWidget): for album in albums: is_owned = album.name in owned_albums + print(f"🎨 Creating card for '{album.name}' - owned: {is_owned}") + card = AlbumCard(album, is_owned) if not is_owned: card.download_requested.connect(self.on_album_download_requested) @@ -1402,6 +1452,10 @@ class ArtistsPage(QWidget): self.albums_status.setText(f"Found {total_count} albums • {owned_count} owned • {missing_count} available for download") + # Debug output + print(f"🎯 UI Update: {owned_count} owned albums found") + print(f"🎯 Owned albums: {list(owned_albums)}") + # Refresh the display with ownership info self.display_albums(self.current_albums, owned_albums)