v2.8
This commit is contained in:
+59
-82
@@ -6,6 +6,7 @@ import requests
|
|||||||
import re
|
import re
|
||||||
from packaging import version
|
from packaging import version
|
||||||
import json
|
import json
|
||||||
|
import asyncio
|
||||||
|
|
||||||
from PyQt6.QtWidgets import (
|
from PyQt6.QtWidgets import (
|
||||||
QApplication, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLineEdit,
|
QApplication, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLineEdit,
|
||||||
@@ -17,7 +18,7 @@ from PyQt6.QtCore import Qt, QThread, pyqtSignal, QUrl, QTimer, QTime, QSettings
|
|||||||
from PyQt6.QtGui import QIcon, QTextCursor, QDesktopServices, QPixmap, QBrush
|
from PyQt6.QtGui import QIcon, QTextCursor, QDesktopServices, QPixmap, QBrush
|
||||||
from PyQt6.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
|
from PyQt6.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
|
||||||
|
|
||||||
from getMetadata import get_filtered_data, parse_uri, SpotifyInvalidUrlException
|
from getMetadata import get_filtered_data, parse_uri, SpotifyInvalidUrlException, get_raw_spotify_data
|
||||||
from getTracks import TrackDownloader
|
from getTracks import TrackDownloader
|
||||||
import SquidWTF
|
import SquidWTF
|
||||||
|
|
||||||
@@ -88,17 +89,15 @@ class DownloadWorker(QThread):
|
|||||||
try:
|
try:
|
||||||
downloader = TrackDownloader(self.use_fallback, self.timeout)
|
downloader = TrackDownloader(self.use_fallback, self.timeout)
|
||||||
|
|
||||||
def progress_update(current, total):
|
def progress_update_lucida(current, total, current_overall_progress):
|
||||||
if total > 0:
|
if total > 0:
|
||||||
percent = (current / total) * 100
|
percent = (current / total) * 100
|
||||||
current_mb = current / (1024 * 1024)
|
current_mb = current / (1024 * 1024)
|
||||||
total_mb = total / (1024 * 1024)
|
total_mb = total / (1024 * 1024)
|
||||||
self.progress.emit(f"Download progress: {percent:.2f}% ({current_mb:.2f}MB/{total_mb:.2f}MB)",
|
self.progress.emit(f"Download progress: {percent:.2f}% ({current_mb:.2f}MB/{total_mb:.2f}MB)",
|
||||||
int(percent))
|
current_overall_progress)
|
||||||
else:
|
else:
|
||||||
self.progress.emit(f"Processing metadata...", 0)
|
self.progress.emit(f"Processing metadata...", current_overall_progress)
|
||||||
|
|
||||||
downloader.set_progress_callback(progress_update)
|
|
||||||
|
|
||||||
total_tracks = len(self.tracks)
|
total_tracks = len(self.tracks)
|
||||||
|
|
||||||
@@ -110,12 +109,15 @@ class DownloadWorker(QThread):
|
|||||||
if self.is_stopped:
|
if self.is_stopped:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
current_overall_progress = int((i / total_tracks) * 100)
|
||||||
|
next_overall_progress = int(((i + 1) / total_tracks) * 100)
|
||||||
|
|
||||||
self.progress.emit(f"Starting download ({i+1}/{total_tracks}): {track.title} - {track.artists}",
|
self.progress.emit(f"Starting download ({i+1}/{total_tracks}): {track.title} - {track.artists}",
|
||||||
int((i) / total_tracks * 100))
|
current_overall_progress)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
track_id = track.id
|
track_id = track.id
|
||||||
self.progress.emit(f"Getting track info for ID: {track_id} from {self.service}", 0)
|
self.progress.emit(f"Getting track info for ID: {track_id} from {self.service}", current_overall_progress)
|
||||||
|
|
||||||
if self.is_playlist and self.use_album_subfolders:
|
if self.is_playlist and self.use_album_subfolders:
|
||||||
album_folder = re.sub(r'[<>:"/\\|?*]', '_', track.album)
|
album_folder = re.sub(r'[<>:"/\\|?*]', '_', track.album)
|
||||||
@@ -125,83 +127,68 @@ class DownloadWorker(QThread):
|
|||||||
track_outpath = self.outpath
|
track_outpath = self.outpath
|
||||||
|
|
||||||
if self.service == "qobuz":
|
if self.service == "qobuz":
|
||||||
self.progress.emit(f"Getting track metadata for: {track.title} - {track.artists}", 0)
|
self.progress.emit(f"Getting track metadata for: {track.title} - {track.artists}", current_overall_progress)
|
||||||
|
|
||||||
isrc = None
|
isrc = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from getMetadata import get_raw_spotify_data, parse_uri
|
|
||||||
|
|
||||||
track_url = track.external_urls
|
track_url = track.external_urls
|
||||||
|
self.progress.emit(f"Fetching Spotify metadata for ISRC...", current_overall_progress)
|
||||||
self.progress.emit(f"Fetching Spotify metadata for ISRC...", 0)
|
|
||||||
raw_data = get_raw_spotify_data(track_url)
|
raw_data = get_raw_spotify_data(track_url)
|
||||||
|
|
||||||
if raw_data and "external_ids" in raw_data and "isrc" in raw_data["external_ids"]:
|
if raw_data and "external_ids" in raw_data and "isrc" in raw_data["external_ids"]:
|
||||||
isrc = raw_data["external_ids"]["isrc"]
|
isrc = raw_data["external_ids"]["isrc"]
|
||||||
self.progress.emit(f"Found ISRC from Spotify: {isrc}", 0)
|
self.progress.emit(f"Found ISRC from Spotify: {isrc}", current_overall_progress)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.progress.emit(f"Could not get ISRC from Spotify raw data: {str(e)}", 0)
|
self.progress.emit(f"Could not get ISRC from Spotify raw data: {str(e)}", current_overall_progress)
|
||||||
|
|
||||||
if not isrc:
|
if not isrc:
|
||||||
self.progress.emit(f"No ISRC found, searching by title and artist", 0)
|
self.progress.emit(f"No ISRC found, searching by title and artist", current_overall_progress)
|
||||||
search_query = f"{track.title} {track.artists}"
|
search_query = f"{track.title} {track.artists}"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.progress.emit(f"Searching Qobuz for: {search_query}", 0)
|
self.progress.emit(f"Searching Qobuz for: {search_query}", current_overall_progress)
|
||||||
|
|
||||||
qobuz_track_info = SquidWTF.search_track(track.title, track.artists, strict_match=True, region=self.qobuz_region)
|
qobuz_track_info = SquidWTF.search_track(track.title, track.artists, strict_match=True, region=self.qobuz_region)
|
||||||
|
|
||||||
if qobuz_track_info:
|
if qobuz_track_info:
|
||||||
qobuz_track_id = qobuz_track_info["id"]
|
qobuz_track_id = qobuz_track_info["id"]
|
||||||
self.progress.emit(f"Found track on Qobuz by title search: {qobuz_track_info['title']} - {qobuz_track_info['performer']['name']}", 0)
|
self.progress.emit(f"Found track on Qobuz by title search: {qobuz_track_info['title']} - {qobuz_track_info['performer']['name']}", current_overall_progress)
|
||||||
|
|
||||||
found_artist = qobuz_track_info['performer']['name'].lower()
|
found_artist = qobuz_track_info['performer']['name'].lower()
|
||||||
expected_artist = track.artists.lower()
|
expected_artist = track.artists.lower()
|
||||||
|
|
||||||
if expected_artist not in found_artist and found_artist not in expected_artist:
|
if expected_artist not in found_artist and found_artist not in expected_artist:
|
||||||
self.progress.emit(f"Warning: Artist mismatch! Expected: {track.artists}, Found: {qobuz_track_info['performer']['name']}", 0)
|
self.progress.emit(f"Warning: Artist mismatch! Expected: {track.artists}, Found: {qobuz_track_info['performer']['name']}", current_overall_progress)
|
||||||
raise Exception(f"Artist mismatch: Expected '{track.artists}', found '{qobuz_track_info['performer']['name']}'")
|
raise Exception(f"Artist mismatch: Expected '{track.artists}', found '{qobuz_track_info['performer']['name']}'")
|
||||||
else:
|
else:
|
||||||
raise Exception(f"Could not find track on Qobuz: {track.title} - {track.artists}")
|
raise Exception(f"Could not find track on Qobuz: {track.title} - {track.artists}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.progress.emit(f"Search by title failed: {str(e)}", 0)
|
self.progress.emit(f"Search by title failed: {str(e)}", current_overall_progress)
|
||||||
raise Exception(f"Could not find track on Qobuz: {track.title} - {track.artists}")
|
raise Exception(f"Could not find track on Qobuz: {track.title} - {track.artists}")
|
||||||
else:
|
else:
|
||||||
self.progress.emit(f"Searching Qobuz with ISRC: {isrc}", 0)
|
self.progress.emit(f"Searching Qobuz with ISRC: {isrc}", current_overall_progress)
|
||||||
qobuz_track_info = SquidWTF.get_track_info(isrc, region=self.qobuz_region)
|
qobuz_track_info = SquidWTF.get_track_info(isrc, region=self.qobuz_region)
|
||||||
qobuz_track_id = qobuz_track_info["id"]
|
qobuz_track_id = qobuz_track_info["id"]
|
||||||
self.progress.emit(f"Found track on Qobuz: {qobuz_track_info['title']} - {qobuz_track_info['performer']['name']}", 0)
|
self.progress.emit(f"Found track on Qobuz: {qobuz_track_info['title']} - {qobuz_track_info['performer']['name']}", current_overall_progress)
|
||||||
|
|
||||||
found_artist = qobuz_track_info['performer']['name'].lower()
|
found_artist = qobuz_track_info['performer']['name'].lower()
|
||||||
expected_artist = track.artists.lower()
|
expected_artist = track.artists.lower()
|
||||||
|
|
||||||
if expected_artist not in found_artist and found_artist not in expected_artist:
|
if expected_artist not in found_artist and found_artist not in expected_artist:
|
||||||
self.progress.emit(f"Warning: Artist mismatch! Expected: {track.artists}, Found: {qobuz_track_info['performer']['name']}", 0)
|
self.progress.emit(f"Warning: Artist mismatch! Expected: {track.artists}, Found: {qobuz_track_info['performer']['name']}", current_overall_progress)
|
||||||
|
|
||||||
download_url = SquidWTF.get_download_url(qobuz_track_id, region=self.qobuz_region)
|
download_url = SquidWTF.get_download_url(qobuz_track_id, region=self.qobuz_region)
|
||||||
|
|
||||||
os.makedirs(track_outpath, exist_ok=True)
|
os.makedirs(track_outpath, exist_ok=True)
|
||||||
|
|
||||||
temp_filename = os.path.join(track_outpath, f"temp_{qobuz_track_id}.flac")
|
temp_filename = os.path.join(track_outpath, f"temp_{qobuz_track_id}.flac")
|
||||||
|
self.progress.emit(f"Downloading from Qobuz...", current_overall_progress)
|
||||||
|
|
||||||
self.progress.emit(f"Downloading from Qobuz...", 0)
|
def progress_callback_qobuz(current, total):
|
||||||
|
|
||||||
def progress_callback(current, total):
|
|
||||||
if total > 0:
|
if total > 0:
|
||||||
percent = (current / total) * 100
|
percent = (current / total) * 100
|
||||||
current_mb = current / (1024 * 1024)
|
current_mb = current / (1024 * 1024)
|
||||||
total_mb = total / (1024 * 1024)
|
total_mb = total / (1024 * 1024)
|
||||||
self.progress.emit(f"Download progress: {percent:.2f}% ({current_mb:.2f}MB/{total_mb:.2f}MB)",
|
self.progress.emit(f"Download progress: {percent:.2f}% ({current_mb:.2f}MB/{total_mb:.2f}MB)",
|
||||||
int(percent))
|
current_overall_progress)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
SquidWTF.download_file(download_url, temp_filename, progress_callback)
|
SquidWTF.download_file(download_url, temp_filename, progress_callback_qobuz)
|
||||||
|
|
||||||
if not os.path.exists(temp_filename) or os.path.getsize(temp_filename) == 0:
|
if not os.path.exists(temp_filename) or os.path.getsize(temp_filename) == 0:
|
||||||
raise Exception(f"Downloaded file is empty or does not exist: {temp_filename}")
|
raise Exception(f"Downloaded file is empty or does not exist: {temp_filename}")
|
||||||
|
|
||||||
self.progress.emit(f"Embedding metadata...", 0)
|
self.progress.emit(f"Embedding metadata...", current_overall_progress)
|
||||||
SquidWTF.embed_metadata(temp_filename, qobuz_track_info)
|
SquidWTF.embed_metadata(temp_filename, qobuz_track_info)
|
||||||
|
|
||||||
if (self.is_album or (self.is_playlist and self.use_album_subfolders)) and self.use_track_numbers:
|
if (self.is_album or (self.is_playlist and self.use_album_subfolders)) and self.use_track_numbers:
|
||||||
@@ -217,25 +204,28 @@ class DownloadWorker(QThread):
|
|||||||
os.rename(temp_filename, new_filepath)
|
os.rename(temp_filename, new_filepath)
|
||||||
|
|
||||||
downloaded_file = new_filepath
|
downloaded_file = new_filepath
|
||||||
self.progress.emit(f"File renamed to: {new_filename}", 0)
|
self.progress.emit(f"File renamed to: {new_filename}", current_overall_progress)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.progress.emit(f"Error during download or processing: {str(e)}", 0)
|
self.progress.emit(f"Error during download or processing: {str(e)}", current_overall_progress)
|
||||||
if os.path.exists(temp_filename):
|
if os.path.exists(temp_filename):
|
||||||
try:
|
try:
|
||||||
os.remove(temp_filename)
|
os.remove(temp_filename)
|
||||||
self.progress.emit(f"Removed incomplete download file", 0)
|
self.progress.emit(f"Removed incomplete download file", current_overall_progress)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
raise Exception(f"Failed to download or process file: {str(e)}")
|
raise Exception(f"Failed to download or process file: {str(e)}")
|
||||||
else:
|
else:
|
||||||
import asyncio
|
|
||||||
metadata = asyncio.run(downloader.get_track_info(track_id, self.service))
|
metadata = asyncio.run(downloader.get_track_info(track_id, self.service))
|
||||||
|
|
||||||
self.progress.emit(f"Track info received, starting download process", 0)
|
self.progress.emit(f"Track info received, starting download process", current_overall_progress)
|
||||||
|
|
||||||
is_paused_callback = lambda: self.is_paused
|
is_paused_callback = lambda: self.is_paused
|
||||||
is_stopped_callback = lambda: self.is_stopped
|
is_stopped_callback = lambda: self.is_stopped
|
||||||
|
|
||||||
|
downloader.set_progress_callback(
|
||||||
|
lambda current, total: progress_update_lucida(current, total, current_overall_progress)
|
||||||
|
)
|
||||||
|
|
||||||
downloaded_file = downloader.download(
|
downloaded_file = downloader.download(
|
||||||
metadata,
|
metadata,
|
||||||
track_outpath,
|
track_outpath,
|
||||||
@@ -255,14 +245,14 @@ class DownloadWorker(QThread):
|
|||||||
if os.path.exists(new_filepath):
|
if os.path.exists(new_filepath):
|
||||||
os.remove(new_filepath)
|
os.remove(new_filepath)
|
||||||
os.rename(downloaded_file, new_filepath)
|
os.rename(downloaded_file, new_filepath)
|
||||||
self.progress.emit(f"File renamed to: {new_filename}", 0)
|
self.progress.emit(f"File renamed to: {new_filename}", current_overall_progress)
|
||||||
|
|
||||||
self.progress.emit(f"Successfully downloaded: {track.title} - {track.artists}",
|
self.progress.emit(f"Successfully downloaded: {track.title} - {track.artists}",
|
||||||
int((i + 1) / total_tracks * 100))
|
next_overall_progress)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.failed_tracks.append((track.title, track.artists, str(e)))
|
self.failed_tracks.append((track.title, track.artists, str(e)))
|
||||||
self.progress.emit(f"Failed to download: {track.title} - {track.artists}\nError: {str(e)}",
|
self.progress.emit(f"Failed to download: {track.title} - {track.artists}\nError: {str(e)}",
|
||||||
int((i + 1) / total_tracks * 100))
|
next_overall_progress)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if not self.is_stopped:
|
if not self.is_stopped:
|
||||||
@@ -344,17 +334,14 @@ class ServiceStatusChecker(QThread):
|
|||||||
services_status['tidal'] = current_services.get('tidal', 0) > 0
|
services_status['tidal'] = current_services.get('tidal', 0) > 0
|
||||||
services_status['deezer'] = current_services.get('deezer', 0) > 0
|
services_status['deezer'] = current_services.get('deezer', 0) > 0
|
||||||
|
|
||||||
print("Lucida services status check successful")
|
except json.JSONDecodeError:
|
||||||
except json.JSONDecodeError as e:
|
pass
|
||||||
print(f"Lucida API returned invalid JSON: {e}")
|
except Exception:
|
||||||
except Exception as e:
|
pass
|
||||||
print(f"Error processing Lucida API response: {str(e)}")
|
except requests.exceptions.RequestException:
|
||||||
else:
|
pass
|
||||||
print(f"Lucida API returned status code: {response.status_code}")
|
except Exception:
|
||||||
except requests.exceptions.RequestException as e:
|
pass
|
||||||
print(f"Error connecting to Lucida API: {str(e)}")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Unexpected error checking Lucida services: {str(e)}")
|
|
||||||
|
|
||||||
eu_online = False
|
eu_online = False
|
||||||
us_online = False
|
us_online = False
|
||||||
@@ -362,16 +349,14 @@ class ServiceStatusChecker(QThread):
|
|||||||
try:
|
try:
|
||||||
eu_response = requests.get("https://eu.qobuz.squid.wtf", timeout=5)
|
eu_response = requests.get("https://eu.qobuz.squid.wtf", timeout=5)
|
||||||
eu_online = eu_response.status_code in [200, 304]
|
eu_online = eu_response.status_code in [200, 304]
|
||||||
print(f"SquidWTF (Qobuz EU) status check: {eu_response.status_code} - {'Online' if eu_online else 'Offline'}")
|
except Exception:
|
||||||
except Exception as e:
|
pass
|
||||||
print(f"Error checking EU Qobuz region: {str(e)}")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
us_response = requests.get("https://us.qobuz.squid.wtf", timeout=5)
|
us_response = requests.get("https://us.qobuz.squid.wtf", timeout=5)
|
||||||
us_online = us_response.status_code in [200, 304]
|
us_online = us_response.status_code in [200, 304]
|
||||||
print(f"SquidWTF (Qobuz US) status check: {us_response.status_code} - {'Online' if us_online else 'Offline'}")
|
except Exception:
|
||||||
except Exception as e:
|
pass
|
||||||
print(f"Error checking US Qobuz region: {str(e)}")
|
|
||||||
|
|
||||||
services_status['qobuz'] = eu_online or us_online
|
services_status['qobuz'] = eu_online or us_online
|
||||||
|
|
||||||
@@ -520,12 +505,9 @@ class QobuzRegionComboBox(QComboBox):
|
|||||||
try:
|
try:
|
||||||
response = requests.get(region['url'], timeout=5)
|
response = requests.get(region['url'], timeout=5)
|
||||||
regions_status[region['id']] = response.status_code in [200, 304]
|
regions_status[region['id']] = response.status_code in [200, 304]
|
||||||
print(f"SquidWTF ({region['name']}) status check: {response.status_code} - {'Online' if regions_status[region['id']] else 'Offline'}")
|
except requests.exceptions.RequestException:
|
||||||
except requests.exceptions.RequestException as e:
|
|
||||||
print(f"Error connecting to SquidWTF API ({region['name']}): {str(e)}")
|
|
||||||
regions_status[region['id']] = False
|
regions_status[region['id']] = False
|
||||||
except Exception as e:
|
except Exception:
|
||||||
print(f"Unexpected error checking SquidWTF ({region['name']}): {str(e)}")
|
|
||||||
regions_status[region['id']] = False
|
regions_status[region['id']] = False
|
||||||
|
|
||||||
self.regions_status = regions_status
|
self.regions_status = regions_status
|
||||||
@@ -601,8 +583,8 @@ class SpotiFLACGUI(QWidget):
|
|||||||
if result == QDialog.DialogCode.Accepted:
|
if result == QDialog.DialogCode.Accepted:
|
||||||
QDesktopServices.openUrl(QUrl("https://github.com/afkarxyz/SpotiFLAC/releases"))
|
QDesktopServices.openUrl(QUrl("https://github.com/afkarxyz/SpotiFLAC/releases"))
|
||||||
|
|
||||||
except Exception as e:
|
except Exception:
|
||||||
print(f"Error checking for updates: {e}")
|
pass
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def format_duration(ms):
|
def format_duration(ms):
|
||||||
@@ -1076,6 +1058,7 @@ class SpotiFLACGUI(QWidget):
|
|||||||
|
|
||||||
def save_qobuz_region_setting(self):
|
def save_qobuz_region_setting(self):
|
||||||
region = self.qobuz_region_dropdown.currentData()
|
region = self.qobuz_region_dropdown.currentData()
|
||||||
|
self.qobuz_region = region
|
||||||
self.settings.setValue('qobuz_region', region)
|
self.settings.setValue('qobuz_region', region)
|
||||||
self.settings.sync()
|
self.settings.sync()
|
||||||
self.log_output.append(f"Qobuz region setting saved: {self.qobuz_region_dropdown.currentText()}")
|
self.log_output.append(f"Qobuz region setting saved: {self.qobuz_region_dropdown.currentText()}")
|
||||||
@@ -1355,7 +1338,7 @@ class SpotiFLACGUI(QWidget):
|
|||||||
|
|
||||||
def start_download_worker(self, tracks_to_download, outpath):
|
def start_download_worker(self, tracks_to_download, outpath):
|
||||||
service = self.service_dropdown.currentData()
|
service = self.service_dropdown.currentData()
|
||||||
qobuz_region = self.qobuz_region_dropdown.currentData() if service == "qobuz" else "us"
|
qobuz_region_val = self.qobuz_region_dropdown.currentData() if service == "qobuz" else self.qobuz_region
|
||||||
|
|
||||||
self.worker = DownloadWorker(
|
self.worker = DownloadWorker(
|
||||||
tracks_to_download,
|
tracks_to_download,
|
||||||
@@ -1370,7 +1353,7 @@ class SpotiFLACGUI(QWidget):
|
|||||||
self.use_fallback,
|
self.use_fallback,
|
||||||
service,
|
service,
|
||||||
self.timeout_value,
|
self.timeout_value,
|
||||||
qobuz_region=qobuz_region
|
qobuz_region=qobuz_region_val
|
||||||
)
|
)
|
||||||
self.worker.finished.connect(self.on_download_finished)
|
self.worker.finished.connect(self.on_download_finished)
|
||||||
self.worker.progress.connect(self.update_progress)
|
self.worker.progress.connect(self.update_progress)
|
||||||
@@ -1391,17 +1374,12 @@ class SpotiFLACGUI(QWidget):
|
|||||||
def update_progress(self, message, percentage):
|
def update_progress(self, message, percentage):
|
||||||
if "Download progress:" in message or "Processing metadata..." in message:
|
if "Download progress:" in message or "Processing metadata..." in message:
|
||||||
current_text = self.log_output.toPlainText()
|
current_text = self.log_output.toPlainText()
|
||||||
|
|
||||||
if current_text:
|
if current_text:
|
||||||
lines = current_text.split('\n')
|
lines = current_text.split('\n')
|
||||||
|
if lines and ("Download progress:" in lines[-1] or "Processing metadata..." in lines[-1]):
|
||||||
if "Download progress:" in lines[-1] or "Processing metadata..." in lines[-1]:
|
|
||||||
lines[-1] = message
|
lines[-1] = message
|
||||||
|
|
||||||
new_text = '\n'.join(lines)
|
new_text = '\n'.join(lines)
|
||||||
|
|
||||||
self.log_output.setPlainText(new_text)
|
self.log_output.setPlainText(new_text)
|
||||||
|
|
||||||
self.log_output.moveCursor(QTextCursor.MoveOperation.End)
|
self.log_output.moveCursor(QTextCursor.MoveOperation.End)
|
||||||
else:
|
else:
|
||||||
self.log_output.append(message)
|
self.log_output.append(message)
|
||||||
@@ -1410,8 +1388,7 @@ class SpotiFLACGUI(QWidget):
|
|||||||
else:
|
else:
|
||||||
self.log_output.append(message)
|
self.log_output.append(message)
|
||||||
|
|
||||||
if percentage > 0:
|
self.progress_bar.setValue(percentage)
|
||||||
self.progress_bar.setValue(percentage)
|
|
||||||
|
|
||||||
def stop_download(self):
|
def stop_download(self):
|
||||||
if hasattr(self, 'worker'):
|
if hasattr(self, 'worker'):
|
||||||
|
|||||||
+33
-21
@@ -4,8 +4,20 @@ from datetime import datetime
|
|||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
def _safe_print(*args, **kwargs):
|
||||||
|
if sys.stdout:
|
||||||
|
print(*args, **kwargs)
|
||||||
|
|
||||||
|
def _safe_stdout_write(data_to_write):
|
||||||
|
if sys.stdout:
|
||||||
|
sys.stdout.write(data_to_write)
|
||||||
|
|
||||||
|
def _safe_flush():
|
||||||
|
if sys.stdout:
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
def get_track_info(isrc, region="us"):
|
def get_track_info(isrc, region="us"):
|
||||||
print(f"Search: {isrc}")
|
_safe_print(f"Search: {isrc}")
|
||||||
base_url = f"https://{region}.qobuz.squid.wtf"
|
base_url = f"https://{region}.qobuz.squid.wtf"
|
||||||
url = f"{base_url}/api/get-music?q={isrc}&offset=0"
|
url = f"{base_url}/api/get-music?q={isrc}&offset=0"
|
||||||
response = requests.get(url)
|
response = requests.get(url)
|
||||||
@@ -16,7 +28,7 @@ def get_track_info(isrc, region="us"):
|
|||||||
|
|
||||||
tracks = data["data"]["tracks"]["items"]
|
tracks = data["data"]["tracks"]["items"]
|
||||||
if not tracks:
|
if not tracks:
|
||||||
print(f"Not Found: {isrc}")
|
_safe_print(f"Not Found: {isrc}")
|
||||||
raise Exception(f"No tracks found for ISRC: {isrc}")
|
raise Exception(f"No tracks found for ISRC: {isrc}")
|
||||||
|
|
||||||
track = None
|
track = None
|
||||||
@@ -26,14 +38,14 @@ def get_track_info(isrc, region="us"):
|
|||||||
break
|
break
|
||||||
|
|
||||||
if not track:
|
if not track:
|
||||||
print(f"Not Found: {isrc}")
|
_safe_print(f"Not Found: {isrc}")
|
||||||
raise Exception(f"No track with matching ISRC: {isrc}")
|
raise Exception(f"No track with matching ISRC: {isrc}")
|
||||||
|
|
||||||
print(f"Found: {track['title']} - {track['performer']['name']}")
|
_safe_print(f"Found: {track['title']} - {track['performer']['name']}")
|
||||||
return track
|
return track
|
||||||
|
|
||||||
def search_track(title, artist, strict_match=False, region="us"):
|
def search_track(title, artist, strict_match=False, region="us"):
|
||||||
print(f"Search by title/artist: {title} - {artist}")
|
_safe_print(f"Search by title/artist: {title} - {artist}")
|
||||||
|
|
||||||
search_query = f"{title} {artist}".replace("feat.", "").replace("ft.", "")
|
search_query = f"{title} {artist}".replace("feat.", "").replace("ft.", "")
|
||||||
|
|
||||||
@@ -47,7 +59,7 @@ def search_track(title, artist, strict_match=False, region="us"):
|
|||||||
|
|
||||||
tracks = data["data"]["tracks"]["items"]
|
tracks = data["data"]["tracks"]["items"]
|
||||||
if not tracks:
|
if not tracks:
|
||||||
print(f"Not Found: {title} - {artist}")
|
_safe_print(f"Not Found: {title} - {artist}")
|
||||||
raise Exception(f"No tracks found for: {title} - {artist}")
|
raise Exception(f"No tracks found for: {title} - {artist}")
|
||||||
|
|
||||||
best_match = None
|
best_match = None
|
||||||
@@ -60,7 +72,7 @@ def search_track(title, artist, strict_match=False, region="us"):
|
|||||||
|
|
||||||
if title_lower == item_title and (artist_lower in item_artist or item_artist in artist_lower):
|
if title_lower == item_title and (artist_lower in item_artist or item_artist in artist_lower):
|
||||||
best_match = item
|
best_match = item
|
||||||
print(f"Found exact title match with artist: {item['title']} - {item['performer']['name']}")
|
_safe_print(f"Found exact title match with artist: {item['title']} - {item['performer']['name']}")
|
||||||
break
|
break
|
||||||
|
|
||||||
if not best_match and not strict_match:
|
if not best_match and not strict_match:
|
||||||
@@ -70,24 +82,24 @@ def search_track(title, artist, strict_match=False, region="us"):
|
|||||||
|
|
||||||
if title_lower in item_title and (artist_lower in item_artist or item_artist in artist_lower):
|
if title_lower in item_title and (artist_lower in item_artist or item_artist in artist_lower):
|
||||||
best_match = item
|
best_match = item
|
||||||
print(f"Found partial match: {item['title']} - {item['performer']['name']}")
|
_safe_print(f"Found partial match: {item['title']} - {item['performer']['name']}")
|
||||||
break
|
break
|
||||||
|
|
||||||
if strict_match and best_match:
|
if strict_match and best_match:
|
||||||
item_artist = best_match["performer"]["name"].lower()
|
item_artist = best_match["performer"]["name"].lower()
|
||||||
if artist_lower not in item_artist and item_artist not in artist_lower:
|
if artist_lower not in item_artist and item_artist not in artist_lower:
|
||||||
print(f"Artist mismatch in strict mode: Expected '{artist}', found '{best_match['performer']['name']}'")
|
_safe_print(f"Artist mismatch in strict mode: Expected '{artist}', found '{best_match['performer']['name']}'")
|
||||||
best_match = None
|
best_match = None
|
||||||
|
|
||||||
if not best_match and not strict_match and tracks:
|
if not best_match and not strict_match and tracks:
|
||||||
best_match = tracks[0]
|
best_match = tracks[0]
|
||||||
print(f"No good match, using first result: {best_match['title']} - {best_match['performer']['name']}")
|
_safe_print(f"No good match, using first result: {best_match['title']} - {best_match['performer']['name']}")
|
||||||
|
|
||||||
if not best_match:
|
if not best_match:
|
||||||
print(f"Not Found: {title} - {artist}")
|
_safe_print(f"Not Found: {title} - {artist}")
|
||||||
raise Exception(f"No suitable track found for: {title} - {artist}")
|
raise Exception(f"No suitable track found for: {title} - {artist}")
|
||||||
|
|
||||||
print(f"Found by title search: {best_match['title']} - {best_match['performer']['name']}")
|
_safe_print(f"Found by title search: {best_match['title']} - {best_match['performer']['name']}")
|
||||||
return best_match
|
return best_match
|
||||||
|
|
||||||
def get_download_url(track_id, region="us"):
|
def get_download_url(track_id, region="us"):
|
||||||
@@ -106,7 +118,7 @@ def download_file(url, filename, progress_callback=None):
|
|||||||
if directory and not os.path.exists(directory):
|
if directory and not os.path.exists(directory):
|
||||||
try:
|
try:
|
||||||
os.makedirs(directory, exist_ok=True)
|
os.makedirs(directory, exist_ok=True)
|
||||||
print(f"Created directory: {directory}")
|
_safe_print(f"Created directory: {directory}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise Exception(f"Failed to create directory {directory}: {str(e)}")
|
raise Exception(f"Failed to create directory {directory}: {str(e)}")
|
||||||
|
|
||||||
@@ -135,11 +147,11 @@ def download_file(url, filename, progress_callback=None):
|
|||||||
progress_callback(downloaded, total_size)
|
progress_callback(downloaded, total_size)
|
||||||
elif total_size > 0:
|
elif total_size > 0:
|
||||||
progress = (downloaded / total_size) * 100
|
progress = (downloaded / total_size) * 100
|
||||||
sys.stdout.write(f"\rProgress Download: {progress:.1f}%")
|
_safe_stdout_write(f"\rProgress Download: {progress:.1f}%")
|
||||||
sys.stdout.flush()
|
_safe_flush()
|
||||||
|
|
||||||
if total_size > 0:
|
if total_size > 0 and not progress_callback:
|
||||||
sys.stdout.write("\n")
|
_safe_stdout_write("\n")
|
||||||
|
|
||||||
if not os.path.exists(filename) or os.path.getsize(filename) == 0:
|
if not os.path.exists(filename) or os.path.getsize(filename) == 0:
|
||||||
raise Exception(f"Download failed: File {filename} is empty or does not exist")
|
raise Exception(f"Download failed: File {filename} is empty or does not exist")
|
||||||
@@ -149,7 +161,7 @@ def download_file(url, filename, progress_callback=None):
|
|||||||
if os.path.exists(filename):
|
if os.path.exists(filename):
|
||||||
try:
|
try:
|
||||||
os.remove(filename)
|
os.remove(filename)
|
||||||
print(f"Removed incomplete file: {filename}")
|
_safe_print(f"Removed incomplete file: {filename}")
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
raise Exception(f"Download failed: {str(e)}")
|
raise Exception(f"Download failed: {str(e)}")
|
||||||
@@ -159,7 +171,7 @@ def embed_metadata(filename, track_info):
|
|||||||
raise Exception(f"Cannot embed metadata: File {filename} does not exist")
|
raise Exception(f"Cannot embed metadata: File {filename} does not exist")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
print("Embedding Tags...")
|
_safe_print("Embedding Tags...")
|
||||||
audio = FLAC(filename)
|
audio = FLAC(filename)
|
||||||
audio.clear()
|
audio.clear()
|
||||||
|
|
||||||
@@ -190,7 +202,7 @@ def embed_metadata(filename, track_info):
|
|||||||
|
|
||||||
audio.add_picture(picture)
|
audio.add_picture(picture)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Warning: Could not add cover image: {str(e)}")
|
_safe_print(f"Warning: Could not add cover image: {str(e)}")
|
||||||
|
|
||||||
audio.save()
|
audio.save()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -206,7 +218,7 @@ def download_cover_image(url):
|
|||||||
|
|
||||||
def main():
|
def main():
|
||||||
try:
|
try:
|
||||||
isrc = "USUM72409273"
|
isrc = "USQX92500261"
|
||||||
region = "us"
|
region = "us"
|
||||||
|
|
||||||
track_info = get_track_info(isrc, region)
|
track_info = get_track_info(isrc, region)
|
||||||
|
|||||||
Reference in New Issue
Block a user