From ed91099a1c6e5f8161fe7f69e151aa627d942289 Mon Sep 17 00:00:00 2001 From: Broque Thomas Date: Thu, 7 Aug 2025 21:43:03 -0700 Subject: [PATCH] enable quality pref. --- config/__pycache__/settings.cpython-310.pyc | Bin 0 -> 4204 bytes config/__pycache__/settings.cpython-312.pyc | Bin 6332 -> 6622 bytes config/settings.py | 4 + .../soulseek_client.cpython-312.pyc | Bin 58689 -> 61183 bytes core/soulseek_client.py | 88 ++++++++++++++++-- ui/pages/__pycache__/artists.cpython-312.pyc | Bin 241866 -> 243020 bytes ui/pages/__pycache__/settings.cpython-312.pyc | Bin 68694 -> 69845 bytes ui/pages/__pycache__/sync.cpython-312.pyc | Bin 235604 -> 236882 bytes ui/pages/artists.py | 23 ++++- ui/pages/settings.py | 43 +++++++-- ui/pages/sync.py | 28 +++++- 11 files changed, 166 insertions(+), 20 deletions(-) create mode 100644 config/__pycache__/settings.cpython-310.pyc diff --git a/config/__pycache__/settings.cpython-310.pyc b/config/__pycache__/settings.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3dd0e12a4ac67d35548071dacd85f56b190b6326 GIT binary patch literal 4204 zcma)9TW=f36`q;B@FJ43BwM!S*r8p!Dcgc-0`!);u2VY>iZ)767f4IA-BO&Pw9;~y zo?TiIK@~;iJ{EmxpPB;FqyIpE%)a)?&v{Fcs^6JiD&$H+LW09NXU^=L^PS6_jq3F( z!{^`6|FQkYI%EGKV)kQ!c!*#8D-4oJ9ryDIFQg^y7f#2Pj&eY`vh>34 zxUwuOFIcA}t8xMVW%-7zD@!(11#*?XD;MP@?5Y~>5;(S8mMhq~fc2WP=*ia^o_ASZ z0k>Y@uF9+6HVWKp@;bPSIk%EOVNKou_fkRjHF*=!0na9414Q{D!3IhO&) zXmm$@4csf>lJ$4xJy5H%2B5!@)*nS--`{$_8+Nyp{%pBt$o1^6y%R@ax{wjxkdPKL zFw>lVj|k~I7!o$&hkU|3?lX)+(r8WCk$ud_^-tMLzR_~D3t3&kaQbZ7O}c4Ag+0A* zyzK3&{j}xZ4gO+Ec;Q)hZe3u!mqe{n(h% zW@G%{F`2L(w!y&GyB6b_CJ{2kCV&NCb@|P2Rj?%#dZN!p*K^p zx9R^0=CmrAAKYPAt1wAj^|2o(acW0H6{faUU8x-ym)5pasB||`#xu^I_7jylz3o9H zTefbHNEtr!`hK8N-cKzM#z)z3i3C=>EtPol+R)B;6+?}e;R2W6JtxqyE%^mhn_AhbB|IC3dDC3>Q1D@DN&V(OR@Vc`I zQ_uqAd$Sl5uVrnFZ)LiT01FCJlT2@hQPS*3qfoxxOu25A47{2QOqZTOU?WPN(DJ)l zM_OMczHPG1rdZbtV7wFtPkj^f0)!g%n62R#Q;DwfOS~Z({LaaEtpL+Z;#`c+V>|;X zYoZoWwhR=+4xwUG)Jp5Gh|qJexN!yxdn4t0uLygp&3-5mN=S3EF0-GCLu$*>U}!AV zcWH$(cqTe(Ufg}I@@&72wV298gI_++z8TRCeG77FNveK#6eK;mo~9pq0wtefu9&_(yAZ(VVrcso`R9Muv%{BK~hYA6GLhv9pcn^-VH`bkKLl}7Q*@-rl9IKiBs^mg`!OSm}&#r zd`Xm11FF0xP>KsyLv}8XsS-~e`)LgXRE1-Gb}TX{mEvKP`2GEtT)%~7{1LQ89hRw_ z$ zd-janmmge?qfrnmwR`d5E5`a} z&{sH?+KJ6>tS&y**gh4@ZU5Qn>F3y9ko|HcNg{I?T9ZdA)Qn?DhI91`C*qaxf z(|~eenh){sK~O%RhroSCw;%cklge37ab!~oJmgWEIt)$};^E45_^t zMS;oDypPb|r?vYuJfNYFF?B!Au^MS5KY&Dh4Fi>qGH;-D5GOSu{#Uca=QUgWcYI?$ zkLGsIK}aQq?hA8QP$1IPc!Y+|08q~<164lHfLhBb15`dYFiH0lYQahy-rxrE5z5nh_P3BWJ@A@X zqxS3bV%;gopF!+2=)Nez2q!AS*0}wiN}9>GYL1YJZ^X@^R(+-6wPtrD{iykD1Z9%_ zW}f}y)oB@Mn{_W=Pwjrt?OmKkt5h6vSuZ;SJ*MiBX{L~i@!k0#IDbQnYib=ewN|aG z^qvrQ2g>u(s^_829R)P6dFH5m#flTHmmr@yoBN51_3QKiYSJ{_irS)q?jQYK8c3T$ z{Ofm!XR7YIG&AlqRp5OhDZXbiz~lnmmzv&i=@&OKlo=X9w_GciuG!1A)!M2x{Wh%C z`f|xgw`$t7VIqoCH`yOjX)+2KCCQKJih2Etwsd#6z2Anf@Q$6e|0R0)3;fOO)r}5L l8$ENkdxPxN@jNZl^8F0B2b5fJ*OA~W4ODYwLQ%g!R^U|xtpp0TYuzWmJ{uWDGPGa)p zgM4C9V(FAs11iRr0Dde~JZ1Km|D0wfw39`FnIS9Dff zkTBSwcA4Mu0*B>fZ!txn|8m7znTp~kzZa8Z{5qLW{JG#~23A(C4}46lRx6mlFaW8^ zzr?eoQh}xv34;hhAn}XCCO1E&G$+-rD0_09gsNm5Bcs(v1~BQs%pfE`S-z1EtQ-K# Ct#R%E delta 168 zcmca-yvLC5G%qg~0}#x)Qk-#-XCmKGfqy{WbcR%hD8>|qD5ezAiH}to#U^tyo?@wF z*A(CUn6Xl1a-z5lP^?b8Rc0kakuXq0ksyfRgc0mOM)AwZDiY6Gzc8?}a!rm$%sAX&l0AS6FozL5{C8~}C{DKr28 diff --git a/config/settings.py b/config/settings.py index bda94eb..cd9331c 100644 --- a/config/settings.py +++ b/config/settings.py @@ -95,5 +95,9 @@ class ConfigManager: 'plex': bool(self.get('plex.base_url')) and bool(self.get('plex.token')), 'soulseek': bool(self.get('soulseek.slskd_url')) } + + def get_quality_preference(self) -> str: + """Get the user's preferred audio quality setting""" + return self.get('settings.audio_quality', 'flac') config_manager = ConfigManager() \ No newline at end of file diff --git a/core/__pycache__/soulseek_client.cpython-312.pyc b/core/__pycache__/soulseek_client.cpython-312.pyc index a836a138eedd5877b85b467de11a93146506dc51..71dc380bed953a2c549b0e36829d3d478596509b 100644 GIT binary patch delta 8009 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 CD+Ve6 delta 6009 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 OS?t&F9>QX2(*Faho4W@9 diff --git a/core/soulseek_client.py b/core/soulseek_client.py index 2deb03d..5df5400 100644 --- a/core/soulseek_client.py +++ b/core/soulseek_client.py @@ -1137,15 +1137,19 @@ class SoulseekClient: logger.warning(f"No results found for: {query}") return None - preferred_results = [r for r in results if r.quality.lower() == preferred_quality.lower()] + # Use the new quality filtering + filtered_results = self.filter_results_by_quality_preference(results, preferred_quality) - if preferred_results: - best_result = preferred_results[0] - else: - best_result = results[0] - logger.info(f"Preferred quality {preferred_quality} not found, using {best_result.quality}") - - logger.info(f"Downloading: {best_result.filename} ({best_result.quality}) from {best_result.username}") + if not filtered_results: + logger.warning(f"No suitable quality results found for: {query}") + return None + + best_result = filtered_results[0] + quality_info = f"{best_result.quality.upper()}" + if best_result.bitrate: + quality_info += f" {best_result.bitrate}kbps" + + logger.info(f"Downloading: {best_result.filename} ({quality_info}) from {best_result.username}") return await self.download(best_result.username, best_result.filename, best_result.size) async def check_connection(self) -> bool: @@ -1160,6 +1164,74 @@ class SoulseekClient: logger.debug(f"Connection check failed: {e}") return False + def filter_results_by_quality_preference(self, results: List[TrackResult], preferred_quality: str) -> List[TrackResult]: + """ + Filter and sort results by quality preference with smart fallback. + Prefers exact match, then higher quality, then lower quality. + """ + if not results: + return [] + + # Normalize preference to match our quality strings + quality_map = { + 'flac': 'flac', + 'mp3_320': ('mp3', 320), + 'mp3_256': ('mp3', 256), + 'mp3_192': ('mp3', 192), + 'any': 'any' + } + + if preferred_quality not in quality_map: + return results # Return all if unknown preference + + if preferred_quality == 'any': + # Sort by quality score for "any" preference + return sorted(results, key=lambda x: x.quality_score, reverse=True) + + # Separate results by quality categories + exact_matches = [] + higher_quality = [] + lower_quality = [] + + if preferred_quality == 'flac': + for result in results: + if result.quality.lower() == 'flac': + exact_matches.append(result) + elif result.quality.lower() == 'mp3' and result.bitrate and result.bitrate >= 320: + higher_quality.append(result) # High-quality MP3 as fallback + else: + lower_quality.append(result) + else: + # MP3 preference with specific bitrate + pref_format, pref_bitrate = quality_map[preferred_quality] + + for result in results: + if result.quality.lower() == 'flac': + higher_quality.append(result) # FLAC is always higher quality + elif result.quality.lower() == pref_format: + if result.bitrate: + if result.bitrate == pref_bitrate: + exact_matches.append(result) + elif result.bitrate > pref_bitrate: + higher_quality.append(result) + else: + lower_quality.append(result) + else: + exact_matches.append(result) # Unknown bitrate, assume match + else: + lower_quality.append(result) + + # Sort each category by quality score and upload speed + def sort_key(result): + return (result.quality_score, result.upload_speed) + + exact_matches.sort(key=sort_key, reverse=True) + higher_quality.sort(key=sort_key, reverse=True) + lower_quality.sort(key=sort_key, reverse=True) + + # Return in preference order: exact > higher > lower + return exact_matches + higher_quality + lower_quality + async def get_session_info(self) -> Optional[Dict[str, Any]]: """Get slskd session information including version""" if not self.base_url: diff --git a/ui/pages/__pycache__/artists.cpython-312.pyc b/ui/pages/__pycache__/artists.cpython-312.pyc index e208ce274f548af41cea8747a55967756e65f962..d278e664f91caf46b8593b47f23af99250ecb80a 100644 GIT binary patch delta 5154 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 delta 4325 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 Q4Mf{M>?VW1oQux?2c4-zivR!s delta 3675 zcmai03v`s#5&mai+4pX;k8GBNY{>H15{v{10hG6>5tC31EubVJ8}l#^`Lhu>*(Jf? z;c@B#nbJk060FBop=$Vz5d>|l@)leT4W^*<*dwAR2ow)Ns_mV-*^nZT7f+WuWYH9f1$(p!BW{;rF3@bQ3Yfmyluz4`)pWQWXJv@ zo5P>vPf8bj88m?&w;;NMYF{RsJR{pFk2rv^MDQjB)vMM1q{#m& zSS36nUm~mmk5lrM$N;}xF%FWX^wM#Fg8Vl11(mA*RT_!>_x1b zpS9w+R=wG4@|vsdHHkqP2mx8uL|mw`Opaf#ST8&v^CqlS?_?~|1A^bi+LRU12HPa} zrAPIuTkz#NM@B&YqgL61^3`&`&CCB|T4hK5$`0xJN~a7~quu9%ES%zVO&cNFSxoV_ z1h=rH#YrShCTR*m4#6~n=>)eD+(s}1pYS=c(>HVa0;cw9>nm%^tIHNu)cM3aD1?$( zJxVc!YZLHQWl)#8oH2p6|#{5l*CN z3bgVQW1Iy_`3Fj6ffnulZ~~7(WVu{oDuvI(YyNa-l~NiffG(yIENm)*-%FiMbHTyk^5Y!UX5nQAhHj=Ca4-u>+ z5HVw?#Pb+sq+oq2Z)Hh!UAfqT=3RxxR&J_$uxN#^R(ulcccrTyXQoCMUVPC7^Q8T| z%nG<0kH7jQeCS5a?piZX_!WvDX%?>AJ#zdarhC+p?a3RiCI{}t`|}#95GH@L*R*(wrHjK8A&TE|`uF_l5Bck& z{sE!L614{S@&9tFRS^)XJXmscG&f{@@pJiU9lr6=0PI`BSfVGW3+T|?Z%yzTyvE_r zKtNY@6T%D&)~vD6B3an|Ub2k2vW!WeOpAh+vS#l+eT-VZGGIxI1%vPKa$eoe_$=1q zBj;^N`k4AnLkk-MhT;146bUaptH-8&meIx-0?g3~jDX}w&Vs-)$l>X|lFHNL9LS@_ zNnZ^2=Er^dKRlQMrk~=$G|U4l^wUROV7ie5BV|sMgZ+x`apxHNg5n{y-Q4ov|#N*~=iFo~UGsNRRkEh{;a|woo z7=9DbcsUJg&beSDK6_y#{z+j~l?z_WbATEBY513O7A)AV$L#(@WZVsHZyQeSx8lze zr-KCErESj{2BjA+xbVz-IJ0*=8QWx(XaTDfy4;EanNR;U$k+o8vO2kgIC zVj?f`o@1;*5wZVZ8h?qCVwOxETGAPC4sYTlnk!?L_`^0x!GE=mr=?jV+-uW*abZ3g z2U^*svRwyuPT2!jBKsxfWQ4kzga0?Tv-D&<)5^no1-sGR&fZ6-j`0qQ1W#(7coYp+ z)8H6pUY$DCqwG`6Uod-aqkdA>&8`PmJXG(xr(jZ}K5KGzj5cv<){xdGpIz%02hewQ zJRr7TZIFErm^e@c$E8OH^fGoS-Sq7Q?EW^R`9nH!~S?A~O-Vo||=Gf!(U{kt)$P6fO4Eyn!s)-*fP+H~s;r_{Dy^~kV#f?dfho;cg{!dX+7%cL+0o2?saE}-PyhFSz-6AcgOe6xi>uL z-rhM&!*iDQsF$5p+k4fnu-et0(xc8ib%StQ3nnb21T7^`}M##|wX5j?OkG{*>I zbMlCqAAA)bBU=+efS{RxZZYCE0^Wu0&<-P5Y`j$}wwUN97(;L-beIK(7ebeekZByF zEQHGikfU4(mwgb*G(n!KP^PqBt_`)CU<{OpUNS+xZTOl-A1={KP!TekVF7qT%gj(2 SUtt3M<|*F@^e?*D`2PSps3%hZ diff --git a/ui/pages/__pycache__/sync.cpython-312.pyc b/ui/pages/__pycache__/sync.cpython-312.pyc index 6489d90e838edce146c476a6b7aaf97e07dfbaed..45bd8e50364ad55234eff26c193a38a63ed7bcf7 100644 GIT binary patch delta 22793 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 F{tH7OLHz&# delta 21775 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 0: + self.quality_combo.setCurrentIndex(index) + except Exception as e: QMessageBox.warning(self, "Error", f"Failed to load configuration: {e}") @@ -604,6 +620,20 @@ class SettingsPage(QWidget): max_workers = int(self.max_workers_combo.currentText()) config_manager.set('database.max_workers', max_workers) + # Save Quality preference + if hasattr(self, 'quality_combo'): + quality_text = self.quality_combo.currentText() + # Map combo box text to config values + config_mapping = { + 'FLAC': 'flac', + '320 kbps MP3': 'mp3_320', + '256 kbps MP3': 'mp3_256', + '192 kbps MP3': 'mp3_192', + 'Any': 'any' + } + config_value = config_mapping.get(quality_text, 'flac') + config_manager.set('settings.audio_quality', config_value) + # Emit signals for path changes to update other pages immediately self.settings_changed.emit('soulseek.download_path', self.download_path_input.text()) self.settings_changed.emit('soulseek.transfer_path', self.transfer_path_input.text()) @@ -1218,14 +1248,15 @@ class SettingsPage(QWidget): quality_label = QLabel("Preferred Quality:") quality_label.setStyleSheet("color: #ffffff; font-size: 12px;") - quality_combo = QComboBox() - quality_combo.addItems(["FLAC", "320 kbps MP3", "256 kbps MP3", "192 kbps MP3", "Any"]) - quality_combo.setCurrentText("FLAC") - quality_combo.setStyleSheet(self.get_combo_style()) - quality_combo.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed) + self.quality_combo = QComboBox() + self.quality_combo.addItems(["FLAC", "320 kbps MP3", "256 kbps MP3", "192 kbps MP3", "Any"]) + self.quality_combo.setCurrentText("FLAC") + self.quality_combo.setStyleSheet(self.get_combo_style()) + self.quality_combo.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed) + self.form_inputs['settings.audio_quality'] = self.quality_combo quality_layout.addWidget(quality_label) - quality_layout.addWidget(quality_combo) + quality_layout.addWidget(self.quality_combo) # Download path path_container = QVBoxLayout() diff --git a/ui/pages/sync.py b/ui/pages/sync.py index d6e64f4..cbe6e17 100644 --- a/ui/pages/sync.py +++ b/ui/pages/sync.py @@ -291,12 +291,13 @@ class TrackDownloadWorkerSignals(QObject): class TrackDownloadWorker(QRunnable): """Background worker to download individual tracks via Soulseek""" - def __init__(self, spotify_track, soulseek_client, download_index, track_index): + def __init__(self, spotify_track, soulseek_client, download_index, track_index, quality_preference=None): super().__init__() self.spotify_track = spotify_track self.soulseek_client = soulseek_client self.download_index = download_index self.track_index = track_index + self.quality_preference = quality_preference or 'flac' self.signals = TrackDownloadWorkerSignals() self._cancelled = False @@ -337,7 +338,7 @@ class TrackDownloadWorker(QRunnable): try: download_id = loop.run_until_complete( - self.soulseek_client.search_and_download_best(query) + self.soulseek_client.search_and_download_best(query, self.quality_preference) ) if download_id: break # Success - stop trying other queries @@ -4628,15 +4629,34 @@ class DownloadMissingTracksModal(QDialog): print(f"❌ Artist '{spotify_artist_name}' NOT found in path: '{slskd_full_path}'. Discarding candidate.") if verified_candidates: + # Apply quality preference filtering before returning + from config.settings import config_manager + quality_preference = config_manager.get_quality_preference() + + # Filter candidates by quality preference with smart fallback + if hasattr(self.parent_page, 'soulseek_client'): + quality_filtered = self.parent_page.soulseek_client.filter_results_by_quality_preference( + verified_candidates, quality_preference + ) + + if quality_filtered: + verified_candidates = quality_filtered + print(f"🎯 Applied quality filtering ({quality_preference}): {len(verified_candidates)} candidates remain") + else: + print(f"⚠️ Quality filtering ({quality_preference}) removed all candidates, keeping originals") + best_confidence = verified_candidates[0].confidence best_version = getattr(verified_candidates[0], 'version_type', 'unknown') - print(f"✅ Found {len(verified_candidates)} VERIFIED matches for '{spotify_track.name}'. Best: {best_confidence:.2f} ({best_version})") + best_quality = getattr(verified_candidates[0], 'quality', 'unknown') + print(f"✅ Found {len(verified_candidates)} VERIFIED matches for '{spotify_track.name}'. Best: {best_confidence:.2f} ({best_version}, {best_quality.upper()})") # Log version breakdown for debugging for candidate in verified_candidates[:3]: # Show top 3 version = getattr(candidate, 'version_type', 'unknown') penalty = getattr(candidate, 'version_penalty', 0.0) - print(f" 🎵 {candidate.confidence:.2f} - {version} (penalty: {penalty:.2f}) - {candidate.filename[:80]}...") + quality = getattr(candidate, 'quality', 'unknown') + bitrate_info = f" {candidate.bitrate}kbps" if hasattr(candidate, 'bitrate') and candidate.bitrate else "" + print(f" 🎵 {candidate.confidence:.2f} - {version} ({quality.upper()}{bitrate_info}) (penalty: {penalty:.2f}) - {candidate.filename[:80]}...") else: print(f"⚠️ No verified matches found for '{spotify_track.name}' after checking file paths.")