diff --git a/.spotify_cache b/.spotify_cache index 25bdac14..a2512cc5 100644 --- a/.spotify_cache +++ b/.spotify_cache @@ -1 +1 @@ -{"access_token": "BQBptSDFOmuCrdFhAT3o2Rd85vMSh-8wxQyZoJf5dhDjTkGaEapSscCO81D_9wW5tsFONfbZaRFPDEmwgLxU5mlPGf5BO19Ngeu7qfwjeSvIsPDgyXTZqpFSTmCcmIQ9nW75V6ixzC48Soc4JG-YZXAZLQN2OCgkSje-L2gHHwuHtJmAnD-3_Lq0CBUJPzP3QR01XFyxXaRWqTg6o3EuqBYOCmNBhKmFGVWTD59-RWOKhsLhcYy3siXcAAYYJtzb", "token_type": "Bearer", "expires_in": 3600, "scope": "user-library-read user-read-private playlist-read-private playlist-read-collaborative user-read-email", "expires_at": 1752163323, "refresh_token": "AQDmfQkPCGObfJeTUIbW1hAAwhSqkuHRA3Qh2dqVYMRh0eCkFMQgPNJDDzF8y-BiaVbj80zePkK_XSfYH1aJutMtNbnsqRKWuxP31BTrMc7pdUdbE7Fma4oH8wpDUKdG3MM"} \ No newline at end of file +{"access_token": "BQBe_MJ7AUBigKGLrL5cDrQcNOcfu9QBWJfHn1YlwGrK7MHkoUaG0q_W6LGKB1qLKTLfqfLvZxBkiU57KA-PfRNM_TLmNOVrje3qqBsy3ILWlO85wNVqK2QEFVEOPdXK9nVyq_UlE9Olo0HiCYRmDShhJwghY7qCAHliDqO8ag2D0f8hZtvf_cf4pDNEq9BRtnGmlOsIMEefpexFt-d86t89Y8qtRQXM_70TsMiQMQEW1XULWWp9ppWAGcmzTYuX", "token_type": "Bearer", "expires_in": 3600, "scope": "user-library-read user-read-private playlist-read-private playlist-read-collaborative user-read-email", "expires_at": 1752167413, "refresh_token": "AQDmfQkPCGObfJeTUIbW1hAAwhSqkuHRA3Qh2dqVYMRh0eCkFMQgPNJDDzF8y-BiaVbj80zePkK_XSfYH1aJutMtNbnsqRKWuxP31BTrMc7pdUdbE7Fma4oH8wpDUKdG3MM"} \ No newline at end of file diff --git a/logs/app.log b/logs/app.log index ed0e9ea5..295e72ea 100644 --- a/logs/app.log +++ b/logs/app.log @@ -5913,3 +5913,115 @@ 2025-07-10 08:58:28 - newmusic.soulseek_client - INFO - search:288 - Found 408 new results (total: 408) at 16.5s 2025-07-10 08:58:28 - newmusic.soulseek_client - INFO - search:298 - Early termination: Found 408 results, stopping search 2025-07-10 08:58:28 - newmusic.soulseek_client - INFO - search:309 - Search completed. Found 408 total results for query: virtual mage +2025-07-10 09:10:08 - newmusic.main - INFO - closeEvent:152 - Closing application... +2025-07-10 09:10:08 - newmusic.main - INFO - closeEvent:157 - Cleaning up Downloads page threads... +2025-07-10 09:10:08 - newmusic.main - INFO - closeEvent:162 - Stopping status monitoring thread... +2025-07-10 09:10:10 - newmusic.main - INFO - closeEvent:167 - Closing Soulseek client... +2025-07-10 09:10:10 - newmusic.main - INFO - closeEvent:173 - Application closed successfully +2025-07-10 09:10:13 - newmusic - INFO - setup_logging:57 - Logging initialized with level: DEBUG +2025-07-10 09:10:13 - newmusic.main - INFO - main:187 - Starting NewMusic application +2025-07-10 09:10:14 - newmusic.spotify_client - INFO - _setup_client:84 - Successfully authenticated with Spotify as broquethomas +2025-07-10 09:10:14 - newmusic.plex_client - INFO - _find_music_library:98 - Found music library: Music +2025-07-10 09:10:14 - newmusic.plex_client - INFO - _setup_client:84 - Successfully connected to Plex server: PLEX-MACHINE +2025-07-10 09:10:14 - newmusic.soulseek_client - INFO - _setup_client:87 - Soulseek client configured with slskd at http://localhost:5030 +2025-07-10 09:10:15 - newmusic.main - INFO - change_page:139 - Changed to page: dashboard +2025-07-10 09:10:15 - newmusic.spotify_client - INFO - get_user_playlists:106 - Fetching tracks for playlist: Aether +2025-07-10 09:10:15 - newmusic.spotify_client - INFO - get_user_playlists:106 - Fetching tracks for playlist: Favorite Artists +2025-07-10 09:10:17 - newmusic.main - INFO - change_page:139 - Changed to page: downloads +2025-07-10 09:10:18 - newmusic.spotify_client - INFO - get_user_playlists:106 - Fetching tracks for playlist: Brittnea +2025-07-10 09:10:19 - newmusic.spotify_client - INFO - get_user_playlists:106 - Fetching tracks for playlist: Baleigh +2025-07-10 09:10:20 - newmusic.soulseek_client - INFO - search:243 - Starting search for: 'virtual mage' +2025-07-10 09:10:20 - newmusic.soulseek_client - DEBUG - search:253 - Search data: {'searchText': 'virtual mage', 'timeout': 30000, 'filterResponses': True, 'minimumResponseFileCount': 1, 'minimumPeerUploadSpeed': 0} +2025-07-10 09:10:20 - newmusic.soulseek_client - DEBUG - search:254 - Making POST request to: http://localhost:5030/api/v0/searches +2025-07-10 09:10:20 - newmusic.soulseek_client - DEBUG - _make_request:109 - Making POST request to: http://localhost:5030/api/v0/searches +2025-07-10 09:10:20 - newmusic.soulseek_client - DEBUG - _make_request:110 - Headers: {'Content-Type': 'application/json', 'X-API-Key': '1234567891234567'} +2025-07-10 09:10:20 - newmusic.soulseek_client - DEBUG - _make_request:112 - JSON payload: {'searchText': 'virtual mage', 'timeout': 30000, 'filterResponses': True, 'minimumResponseFileCount': 1, 'minimumPeerUploadSpeed': 0} +2025-07-10 09:10:21 - newmusic.soulseek_client - DEBUG - _make_request:121 - Response status: 200 +2025-07-10 09:10:21 - newmusic.soulseek_client - DEBUG - _make_request:122 - Response text: {"fileCount":0,"id":"f3e90029-4840-40f9-9187-df5ada8b213e","isComplete":false,"lockedFileCount":0,"responseCount":0,"responses":[],"searchText":"virtual mage","startedAt":"2025-07-10T16:10:21.0269644Z","state":"InProgress","token":139}... +2025-07-10 09:10:21 - newmusic.soulseek_client - INFO - search:267 - Search initiated with ID: f3e90029-4840-40f9-9187-df5ada8b213e +2025-07-10 09:10:21 - newmusic.soulseek_client - DEBUG - search:275 - Polling for results (attempt 1/20) - elapsed: 0.0s +2025-07-10 09:10:21 - newmusic.soulseek_client - DEBUG - _make_request:109 - Making GET request to: http://localhost:5030/api/v0/searches/f3e90029-4840-40f9-9187-df5ada8b213e/responses +2025-07-10 09:10:21 - newmusic.soulseek_client - DEBUG - _make_request:110 - Headers: {'Content-Type': 'application/json', 'X-API-Key': '1234567891234567'} +2025-07-10 09:10:21 - newmusic.soulseek_client - DEBUG - _make_request:121 - Response status: 200 +2025-07-10 09:10:21 - newmusic.soulseek_client - DEBUG - _make_request:122 - Response text: []... +2025-07-10 09:10:21 - newmusic.spotify_client - INFO - get_user_playlists:106 - Fetching tracks for playlist: Extra Music +2025-07-10 09:10:22 - newmusic.spotify_client - INFO - get_user_playlists:106 - Fetching tracks for playlist: Maggi Main +2025-07-10 09:10:22 - newmusic.soulseek_client - DEBUG - search:275 - Polling for results (attempt 2/20) - elapsed: 1.5s +2025-07-10 09:10:22 - newmusic.soulseek_client - DEBUG - _make_request:109 - Making GET request to: http://localhost:5030/api/v0/searches/f3e90029-4840-40f9-9187-df5ada8b213e/responses +2025-07-10 09:10:22 - newmusic.soulseek_client - DEBUG - _make_request:110 - Headers: {'Content-Type': 'application/json', 'X-API-Key': '1234567891234567'} +2025-07-10 09:10:23 - newmusic.soulseek_client - DEBUG - _make_request:121 - Response status: 200 +2025-07-10 09:10:23 - newmusic.soulseek_client - DEBUG - _make_request:122 - Response text: []... +2025-07-10 09:10:24 - newmusic.soulseek_client - DEBUG - search:275 - Polling for results (attempt 3/20) - elapsed: 3.0s +2025-07-10 09:10:24 - newmusic.soulseek_client - DEBUG - _make_request:109 - Making GET request to: http://localhost:5030/api/v0/searches/f3e90029-4840-40f9-9187-df5ada8b213e/responses +2025-07-10 09:10:24 - newmusic.soulseek_client - DEBUG - _make_request:110 - Headers: {'Content-Type': 'application/json', 'X-API-Key': '1234567891234567'} +2025-07-10 09:10:25 - newmusic.soulseek_client - DEBUG - _make_request:121 - Response status: 200 +2025-07-10 09:10:25 - newmusic.soulseek_client - DEBUG - _make_request:122 - Response text: []... +2025-07-10 09:10:26 - newmusic.soulseek_client - DEBUG - search:275 - Polling for results (attempt 4/20) - elapsed: 4.5s +2025-07-10 09:10:26 - newmusic.soulseek_client - DEBUG - _make_request:109 - Making GET request to: http://localhost:5030/api/v0/searches/f3e90029-4840-40f9-9187-df5ada8b213e/responses +2025-07-10 09:10:26 - newmusic.soulseek_client - DEBUG - _make_request:110 - Headers: {'Content-Type': 'application/json', 'X-API-Key': '1234567891234567'} +2025-07-10 09:10:27 - newmusic.soulseek_client - DEBUG - _make_request:121 - Response status: 200 +2025-07-10 09:10:27 - newmusic.soulseek_client - DEBUG - _make_request:122 - Response text: []... +2025-07-10 09:10:28 - newmusic.spotify_client - INFO - get_user_playlists:106 - Fetching tracks for playlist: Broque Main +2025-07-10 09:10:28 - newmusic.soulseek_client - DEBUG - search:275 - Polling for results (attempt 5/20) - elapsed: 6.0s +2025-07-10 09:10:28 - newmusic.soulseek_client - DEBUG - _make_request:109 - Making GET request to: http://localhost:5030/api/v0/searches/f3e90029-4840-40f9-9187-df5ada8b213e/responses +2025-07-10 09:10:28 - newmusic.soulseek_client - DEBUG - _make_request:110 - Headers: {'Content-Type': 'application/json', 'X-API-Key': '1234567891234567'} +2025-07-10 09:10:28 - newmusic.soulseek_client - DEBUG - _make_request:121 - Response status: 200 +2025-07-10 09:10:28 - newmusic.soulseek_client - DEBUG - _make_request:122 - Response text: []... +2025-07-10 09:10:30 - newmusic.soulseek_client - DEBUG - search:275 - Polling for results (attempt 6/20) - elapsed: 7.5s +2025-07-10 09:10:30 - newmusic.soulseek_client - DEBUG - _make_request:109 - Making GET request to: http://localhost:5030/api/v0/searches/f3e90029-4840-40f9-9187-df5ada8b213e/responses +2025-07-10 09:10:30 - newmusic.soulseek_client - DEBUG - _make_request:110 - Headers: {'Content-Type': 'application/json', 'X-API-Key': '1234567891234567'} +2025-07-10 09:10:30 - newmusic.soulseek_client - DEBUG - _make_request:121 - Response status: 200 +2025-07-10 09:10:30 - newmusic.soulseek_client - DEBUG - _make_request:122 - Response text: []... +2025-07-10 09:10:32 - newmusic.soulseek_client - DEBUG - search:275 - Polling for results (attempt 7/20) - elapsed: 9.0s +2025-07-10 09:10:32 - newmusic.soulseek_client - DEBUG - _make_request:109 - Making GET request to: http://localhost:5030/api/v0/searches/f3e90029-4840-40f9-9187-df5ada8b213e/responses +2025-07-10 09:10:32 - newmusic.soulseek_client - DEBUG - _make_request:110 - Headers: {'Content-Type': 'application/json', 'X-API-Key': '1234567891234567'} +2025-07-10 09:10:32 - newmusic.soulseek_client - DEBUG - _make_request:121 - Response status: 200 +2025-07-10 09:10:32 - newmusic.soulseek_client - DEBUG - _make_request:122 - Response text: []... +2025-07-10 09:10:34 - newmusic.soulseek_client - DEBUG - search:275 - Polling for results (attempt 8/20) - elapsed: 10.5s +2025-07-10 09:10:34 - newmusic.soulseek_client - DEBUG - _make_request:109 - Making GET request to: http://localhost:5030/api/v0/searches/f3e90029-4840-40f9-9187-df5ada8b213e/responses +2025-07-10 09:10:34 - newmusic.soulseek_client - DEBUG - _make_request:110 - Headers: {'Content-Type': 'application/json', 'X-API-Key': '1234567891234567'} +2025-07-10 09:10:34 - newmusic.soulseek_client - DEBUG - _make_request:121 - Response status: 200 +2025-07-10 09:10:34 - newmusic.soulseek_client - DEBUG - _make_request:122 - Response text: []... +2025-07-10 09:10:36 - newmusic.soulseek_client - DEBUG - search:275 - Polling for results (attempt 9/20) - elapsed: 12.0s +2025-07-10 09:10:36 - newmusic.soulseek_client - DEBUG - _make_request:109 - Making GET request to: http://localhost:5030/api/v0/searches/f3e90029-4840-40f9-9187-df5ada8b213e/responses +2025-07-10 09:10:36 - newmusic.soulseek_client - DEBUG - _make_request:110 - Headers: {'Content-Type': 'application/json', 'X-API-Key': '1234567891234567'} +2025-07-10 09:10:36 - newmusic.soulseek_client - DEBUG - _make_request:121 - Response status: 200 +2025-07-10 09:10:36 - newmusic.soulseek_client - DEBUG - _make_request:122 - Response text: []... +2025-07-10 09:10:36 - newmusic.spotify_client - INFO - get_user_playlists:113 - Retrieved 7 playlists +2025-07-10 09:10:37 - newmusic.soulseek_client - DEBUG - search:275 - Polling for results (attempt 10/20) - elapsed: 13.5s +2025-07-10 09:10:37 - newmusic.soulseek_client - DEBUG - _make_request:109 - Making GET request to: http://localhost:5030/api/v0/searches/f3e90029-4840-40f9-9187-df5ada8b213e/responses +2025-07-10 09:10:37 - newmusic.soulseek_client - DEBUG - _make_request:110 - Headers: {'Content-Type': 'application/json', 'X-API-Key': '1234567891234567'} +2025-07-10 09:10:38 - newmusic.soulseek_client - DEBUG - _make_request:121 - Response status: 200 +2025-07-10 09:10:38 - newmusic.soulseek_client - DEBUG - _make_request:122 - Response text: []... +2025-07-10 09:10:39 - newmusic.soulseek_client - DEBUG - search:275 - Polling for results (attempt 11/20) - elapsed: 15.0s +2025-07-10 09:10:39 - newmusic.soulseek_client - DEBUG - _make_request:109 - Making GET request to: http://localhost:5030/api/v0/searches/f3e90029-4840-40f9-9187-df5ada8b213e/responses +2025-07-10 09:10:39 - newmusic.soulseek_client - DEBUG - _make_request:110 - Headers: {'Content-Type': 'application/json', 'X-API-Key': '1234567891234567'} +2025-07-10 09:10:40 - newmusic.soulseek_client - DEBUG - _make_request:121 - Response status: 200 +2025-07-10 09:10:40 - newmusic.soulseek_client - DEBUG - _make_request:122 - Response text: [{"fileCount":9,"files":[{"code":1,"extension":"","filename":"FLACs\\by genre\\synthwave\\Virtual Mage\\Virtual Mage - Aether WEB (2020)\\00 Virtual Mage - Aether Folder.auCDtect.txt","size":1168,"isLocked":false},{"code":1,"extension":"","filename":"FLACs\\by genre\\synthwave\\Virtual Mage\\Virtual Mage - Aether WEB (2020)\\00 Virtual Mage - Aether.jpg","size":55497,"isLocked":false},{"bitDepth":16,"code":1,"extension":"","filename":"FLACs\\by genre\\synthwave\\Virtual Mage\\Virtual Mage - Aeth... +2025-07-10 09:10:40 - newmusic.soulseek_client - DEBUG - _process_search_responses:208 - Processing 23 user responses +2025-07-10 09:10:40 - newmusic.soulseek_client - DEBUG - _process_search_responses:213 - User Ultravod has 9 files +2025-07-10 09:10:40 - newmusic.soulseek_client - DEBUG - _process_search_responses:213 - User RuuqoHoosk has 346 files +2025-07-10 09:10:40 - newmusic.soulseek_client - DEBUG - _process_search_responses:213 - User HomotopicCensorship has 3 files +2025-07-10 09:10:40 - newmusic.soulseek_client - DEBUG - _process_search_responses:213 - User KindaRatchet has 1 files +2025-07-10 09:10:40 - newmusic.soulseek_client - DEBUG - _process_search_responses:213 - User belthane has 3 files +2025-07-10 09:10:40 - newmusic.soulseek_client - DEBUG - _process_search_responses:213 - User buk4 has 1 files +2025-07-10 09:10:40 - newmusic.soulseek_client - DEBUG - _process_search_responses:213 - User exsanguinidraculae has 2 files +2025-07-10 09:10:40 - newmusic.soulseek_client - DEBUG - _process_search_responses:213 - User Letham has 3 files +2025-07-10 09:10:40 - newmusic.soulseek_client - DEBUG - _process_search_responses:213 - User leonardoclk has 3 files +2025-07-10 09:10:40 - newmusic.soulseek_client - DEBUG - _process_search_responses:213 - User Dax_VR has 10 files +2025-07-10 09:10:40 - newmusic.soulseek_client - DEBUG - _process_search_responses:213 - User Satani-Vitales has 3 files +2025-07-10 09:10:40 - newmusic.soulseek_client - DEBUG - _process_search_responses:213 - User soulshookt has 3 files +2025-07-10 09:10:40 - newmusic.soulseek_client - DEBUG - _process_search_responses:213 - User FixxAlish has 1 files +2025-07-10 09:10:40 - newmusic.soulseek_client - DEBUG - _process_search_responses:213 - User ruszok has 1 files +2025-07-10 09:10:40 - newmusic.soulseek_client - DEBUG - _process_search_responses:213 - User thinkibettergo has 1 files +2025-07-10 09:10:40 - newmusic.soulseek_client - DEBUG - _process_search_responses:213 - User wonko1987 has 2 files +2025-07-10 09:10:40 - newmusic.soulseek_client - DEBUG - _process_search_responses:213 - User koalabeer has 3 files +2025-07-10 09:10:40 - newmusic.soulseek_client - DEBUG - _process_search_responses:213 - User raspberry2 has 10 files +2025-07-10 09:10:40 - newmusic.soulseek_client - DEBUG - _process_search_responses:213 - User zeroscan has 2 files +2025-07-10 09:10:40 - newmusic.soulseek_client - DEBUG - _process_search_responses:213 - User nightmare_dronemk12 has 3 files +2025-07-10 09:10:40 - newmusic.soulseek_client - DEBUG - _process_search_responses:213 - User ofoijacussa has 0 files +2025-07-10 09:10:40 - newmusic.soulseek_client - DEBUG - _process_search_responses:213 - User Shooby has 6 files +2025-07-10 09:10:40 - newmusic.soulseek_client - DEBUG - _process_search_responses:213 - User Kaya-Sem has 1 files +2025-07-10 09:10:40 - newmusic.soulseek_client - INFO - search:288 - Found 417 new results (total: 417) at 15.0s +2025-07-10 09:10:40 - newmusic.soulseek_client - INFO - search:298 - Early termination: Found 417 results, stopping search +2025-07-10 09:10:40 - newmusic.soulseek_client - INFO - search:309 - Search completed. Found 417 total results for query: virtual mage diff --git a/ui/pages/__pycache__/downloads.cpython-312.pyc b/ui/pages/__pycache__/downloads.cpython-312.pyc index 912efd49..c3c18fc5 100644 Binary files a/ui/pages/__pycache__/downloads.cpython-312.pyc and b/ui/pages/__pycache__/downloads.cpython-312.pyc differ diff --git a/ui/pages/downloads.py b/ui/pages/downloads.py index 225ee0ee..6064ec35 100644 --- a/ui/pages/downloads.py +++ b/ui/pages/downloads.py @@ -362,9 +362,10 @@ class SearchResultItem(QFrame): self.title_label.setFont(QFont("Arial", 11, QFont.Weight.Bold)) # Reduced from 13px to 11px self.title_label.setStyleSheet("color: #ffffff;") self.title_label.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred) - # Ensure text doesn't overflow the label + # Ensure text doesn't overflow the label and allow click-through self.title_label.setWordWrap(False) - self.title_label.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse) + # Remove text selection to allow clicks to propagate to parent widget + self.title_label.setTextInteractionFlags(Qt.TextInteractionFlag.NoTextInteraction) # Expand indicator self.expand_indicator = QLabel("⏵") @@ -386,7 +387,7 @@ class SearchResultItem(QFrame): self.expanded_content = QWidget() expanded_layout = QVBoxLayout(self.expanded_content) expanded_layout.setContentsMargins(0, 0, 0, 0) - expanded_layout.setSpacing(2) # Very tight spacing for dense layout + expanded_layout.setSpacing(1) # Ultra-tight spacing for more compact layout # Artist info self.artist_info = QLabel(primary_info['artist']) @@ -412,28 +413,18 @@ class SearchResultItem(QFrame): bottom_row.setContentsMargins(0, 0, 0, 0) bottom_row.setSpacing(8) - self.user_info = QLabel(f"👤 {self.search_result.username}") + # Apply intelligent path truncation to username/file location + truncated_path = self._truncate_file_path(self.search_result.username, self.search_result.filename) + self.user_info = QLabel(f"👤 {truncated_path}") self.user_info.setFont(QFont("Arial", 9, QFont.Weight.Medium)) # Smaller font self.user_info.setStyleSheet("color: rgba(29, 185, 84, 0.8);") self.speed_indicator = self._create_compact_speed_indicator() - self.quality_score = QLabel(f"★{self.search_result.quality_score:.1f}") - self.quality_score.setAlignment(Qt.AlignmentFlag.AlignCenter) - self.quality_score.setFont(QFont("Arial", 8, QFont.Weight.Bold)) # Smaller font - self.quality_score.setFixedSize(32, 16) # Smaller size - - if self.search_result.quality_score >= 0.9: - self.quality_score.setStyleSheet("color: #1db954; background: rgba(29, 185, 84, 0.15); border-radius: 8px;") - elif self.search_result.quality_score >= 0.7: - self.quality_score.setStyleSheet("color: #ffa500; background: rgba(255, 165, 0, 0.15); border-radius: 8px;") - else: - self.quality_score.setStyleSheet("color: #e22134; background: rgba(226, 33, 52, 0.15); border-radius: 8px;") - + # Add widgets to bottom row (removed misleading star rating) bottom_row.addWidget(self.user_info) bottom_row.addWidget(self.speed_indicator) bottom_row.addStretch() - bottom_row.addWidget(self.quality_score) # Add all expanded content expanded_layout.addWidget(self.artist_info) @@ -536,6 +527,36 @@ class SearchResultItem(QFrame): else: return self.size().expandedTo(self.minimumSize()).boundedTo(self.maximumSize()) + def _truncate_file_path(self, username, filename): + """Truncate file path to show max 3 levels: file + parent + grandparent folder""" + import os + + # If username looks like a simple username (no path separators), return as-is + if '/' not in username and '\\' not in username: + return username + + # Get filename without extension for comparison + file_base = os.path.splitext(os.path.basename(filename))[0] + + # Split path using both Windows and Unix separators + path_parts = username.replace('\\', '/').split('/') + + # Remove empty parts + path_parts = [part for part in path_parts if part.strip()] + + # If path is already short, return as-is + if len(path_parts) <= 3: + return '/'.join(path_parts) + + # Take last 3 components (file + parent + grandparent) + truncated_parts = path_parts[-3:] + + # If we truncated, add ellipsis at the beginning + if len(path_parts) > 3: + return '.../' + '/'.join(truncated_parts) + else: + return '/'.join(truncated_parts) + def _extract_song_info(self): """Extract song title and artist from filename""" filename = self.search_result.filename