diff --git a/SpotiFLAC.py b/SpotiFLAC.py index e2c9c62..d755654 100644 --- a/SpotiFLAC.py +++ b/SpotiFLAC.py @@ -66,13 +66,14 @@ class DownloadWorker(QThread): def progress_update(current, total): if total > 0: percent = (current / total) * 100 - self.progress.emit(f"Download progress: {percent:.2f}% ({current}/{total})", + current_mb = current / (1024 * 1024) + total_mb = total / (1024 * 1024) + self.progress.emit(f"Download progress: {percent:.2f}% ({current_mb:.2f}MB/{total_mb:.2f}MB)", int(percent)) else: self.progress.emit(f"Processing metadata...", 0) downloader.set_progress_callback(progress_update) - downloader.set_filename_format(self.filename_format) total_tracks = len(self.tracks) @@ -102,7 +103,16 @@ class DownloadWorker(QThread): metadata = asyncio.run(downloader.get_track_info(track_id, self.service)) self.progress.emit(f"Track info received, starting download process", 0) - downloaded_file = downloader.download(metadata, track_outpath) + + is_paused_callback = lambda: self.is_paused + is_stopped_callback = lambda: self.is_stopped + + downloaded_file = downloader.download( + metadata, + track_outpath, + is_paused_callback=is_paused_callback, + is_stopped_callback=is_stopped_callback + ) if (self.is_album or (self.is_playlist and self.use_album_subfolders)) and self.use_track_numbers: new_filename = f"{track.track_number:02d} - {self.get_formatted_filename(track)}" @@ -300,7 +310,7 @@ class ServiceComboBox(QComboBox): class SpotiFLACGUI(QWidget): def __init__(self): super().__init__() - self.current_version = "2.2" + self.current_version = "2.3" self.tracks = [] self.reset_state() @@ -719,7 +729,7 @@ class SpotiFLACGUI(QWidget): spacer = QSpacerItem(20, 6, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Fixed) about_layout.addItem(spacer) - footer_label = QLabel("v2.2 | March 2025") + footer_label = QLabel("v2.3 | March 2025") footer_label.setStyleSheet("font-size: 12px; color: palette(text); margin-top: 10px;") about_layout.addWidget(footer_label, alignment=Qt.AlignmentFlag.AlignCenter) @@ -1044,8 +1054,27 @@ class SpotiFLACGUI(QWidget): self.tab_widget.setCurrentWidget(self.process_tab) def update_progress(self, message, percentage): - self.log_output.append(message) - self.log_output.moveCursor(QTextCursor.MoveOperation.End) + if "Download progress:" in message or "Processing metadata..." in message: + current_text = self.log_output.toPlainText() + + if current_text: + lines = current_text.split('\n') + + if "Download progress:" in lines[-1] or "Processing metadata..." in lines[-1]: + lines[-1] = message + + new_text = '\n'.join(lines) + + self.log_output.setPlainText(new_text) + + self.log_output.moveCursor(QTextCursor.MoveOperation.End) + else: + self.log_output.append(message) + else: + self.log_output.append(message) + else: + self.log_output.append(message) + if percentage > 0: self.progress_bar.setValue(percentage) diff --git a/getTracks.py b/getTracks.py index eab8f61..2767fe7 100644 --- a/getTracks.py +++ b/getTracks.py @@ -12,22 +12,14 @@ class TrackDownloader: 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' } self.progress_callback = None - self.filename_format = 'title_artist' self.use_fallback = use_fallback self.base_domain = "lucida.su" if use_fallback else "lucida.to" def set_progress_callback(self, callback): self.progress_callback = callback - - def set_filename_format(self, format_type): - self.filename_format = format_type - def generate_filename(self, metadata): - if self.filename_format == 'artist_title': - filename = f"{metadata['artists']} - {metadata['title']}.flac" - else: - filename = f"{metadata['title']} - {metadata['artists']}.flac" - return self.sanitize_filename(filename) + def generate_filename(self, track_id, service): + return f"{track_id}_{service}.flac" async def get_track_info(self, track_id, service="amazon", use_fallback=None): if use_fallback is None: @@ -42,6 +34,8 @@ class TrackDownloader: if "error" in result: raise Exception(f"Failed to get track info: {result['error']}") + result["track_id"] = track_id + return result def convert_spotify_link(self, spotify_url, target_service="amazon", domain_type="to"): @@ -73,7 +67,7 @@ class TrackDownloader: } session = requests.Session() - session.verify = False + session.verify = True response = session.get( base_url, @@ -129,9 +123,7 @@ class TrackDownloader: "token": { "primary": None, "expiry": None - }, - "title": "Title", - "artists": "Artist" + } } if token: @@ -149,27 +141,18 @@ class TrackDownloader: except Exception as error: return {"error": str(error)} - def sanitize_filename(self, filename): - invalid_chars = '<>:"/\\|?*' - for char in invalid_chars: - filename = filename.replace(char, '') - - filename = ' '.join(filename.split()) - filename = filename.replace(' ,', ',') - filename = filename.replace(',', ', ') - while ' ' in filename: - filename = filename.replace(' ', ' ') - filename = filename.rsplit('.', 1) - filename[0] = filename[0].strip() - return '.'.join(filename) - - def download(self, metadata, output_dir): + def download(self, metadata, output_dir, is_paused_callback=None, is_stopped_callback=None): track_url = metadata['url'] primary_token = metadata['token']['primary'] expiry = metadata['token']['expiry'] + track_id = metadata['track_id'] + service = metadata['service'] print(f"Starting download for: {track_url}") + if is_stopped_callback and is_stopped_callback(): + raise Exception("Download stopped by user") + initial_request = { "account": {"id": "auto", "type": "country"}, "compat": "false", @@ -201,12 +184,20 @@ class TrackDownloader: handoff = initial_response["handoff"] server = initial_response["server"] - file_name = self.generate_filename(metadata) + file_name = self.generate_filename(track_id, service) completion_url = f"https://{server}.{self.base_domain}/api/fetch/request/{handoff}" print("Waiting for track processing to complete") while True: + if is_stopped_callback and is_stopped_callback(): + raise Exception("Download stopped by user") + + while is_paused_callback and is_paused_callback(): + time.sleep(0.1) + if is_stopped_callback and is_stopped_callback(): + raise Exception("Download stopped by user") + completion_response = self.client.get(completion_url, headers=self.headers).json() status = completion_response["status"] @@ -250,6 +241,20 @@ class TrackDownloader: last_update_time = start_time for chunk in response.iter_content(chunk_size=8192): + if is_stopped_callback and is_stopped_callback(): + file.close() + if os.path.exists(file_path): + os.remove(file_path) + raise Exception("Download stopped by user") + + while is_paused_callback and is_paused_callback(): + time.sleep(0.1) + if is_stopped_callback and is_stopped_callback(): + file.close() + if os.path.exists(file_path): + os.remove(file_path) + raise Exception("Download stopped by user") + if chunk: file.write(chunk) downloaded_size += len(chunk)