Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1e7a48d263 | |||
| 0a83a0dd6e | |||
| da429d9410 | |||
| 63211c726b | |||
| 055cb6991a |
@@ -1,12 +1,12 @@
|
||||
[](https://github.com/afkarxyz/SpotiFLAC/releases)
|
||||
|
||||

|
||||

|
||||
|
||||
<div align="center">
|
||||
<b>SpotiFLAC</b> allows you to download Spotify tracks in true FLAC format through services like Tidal, Amazon Music and Deezer with the help of Lucida.
|
||||
</div>
|
||||
|
||||
### [Download](https://github.com/afkarxyz/SpotiFLAC/releases/download/v2.3/SpotiFLAC.exe)
|
||||
### [Download](https://github.com/afkarxyz/SpotiFLAC/releases/download/v2.5/SpotiFLAC.exe)
|
||||
|
||||
#
|
||||
|
||||
@@ -15,15 +15,17 @@ Sometimes, the **download speed** from Lucida can be fast or slow; it varies unp
|
||||
|
||||
## Screenshots
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||
|
||||
> When **Fallback** is enabled, it will use the backup server `Lucida.su`
|
||||
|
||||
|
||||
+51
-19
@@ -55,7 +55,7 @@ class DownloadWorker(QThread):
|
||||
|
||||
def __init__(self, tracks, outpath, is_single_track=False, is_album=False, is_playlist=False,
|
||||
album_or_playlist_name='', filename_format='title_artist', use_track_numbers=True,
|
||||
use_album_subfolders=False, use_fallback=False, service="amazon"):
|
||||
use_album_subfolders=False, use_fallback=False, service="amazon", timeout=30):
|
||||
super().__init__()
|
||||
self.tracks = tracks
|
||||
self.outpath = outpath
|
||||
@@ -68,6 +68,7 @@ class DownloadWorker(QThread):
|
||||
self.use_album_subfolders = use_album_subfolders
|
||||
self.use_fallback = use_fallback
|
||||
self.service = service
|
||||
self.timeout = timeout
|
||||
self.is_paused = False
|
||||
self.is_stopped = False
|
||||
self.failed_tracks = []
|
||||
@@ -81,7 +82,7 @@ class DownloadWorker(QThread):
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
downloader = TrackDownloader(self.use_fallback)
|
||||
downloader = TrackDownloader(self.use_fallback, self.timeout)
|
||||
|
||||
def progress_update(current, total):
|
||||
if total > 0:
|
||||
@@ -330,7 +331,7 @@ class ServiceComboBox(QComboBox):
|
||||
class SpotiFLACGUI(QWidget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.current_version = "2.4"
|
||||
self.current_version = "2.6"
|
||||
self.tracks = []
|
||||
self.reset_state()
|
||||
|
||||
@@ -343,6 +344,7 @@ class SpotiFLACGUI(QWidget):
|
||||
self.use_album_subfolders = self.settings.value('use_album_subfolders', False, type=bool)
|
||||
self.use_fallback = self.settings.value('use_fallback', False, type=bool)
|
||||
self.service = self.settings.value('service', 'amazon')
|
||||
self.timeout_value = self.settings.value('timeout_value', 30, type=int)
|
||||
self.check_for_updates = self.settings.value('check_for_updates', True, type=bool)
|
||||
|
||||
self.elapsed_time = QTime(0, 0, 0)
|
||||
@@ -586,7 +588,7 @@ class SpotiFLACGUI(QWidget):
|
||||
output_layout.setSpacing(5)
|
||||
|
||||
output_label = QLabel('Output Directory')
|
||||
output_label.setStyleSheet("font-weight: bold; color: palette(text);")
|
||||
output_label.setStyleSheet("font-weight: bold;")
|
||||
output_layout.addWidget(output_label)
|
||||
|
||||
output_dir_layout = QHBoxLayout()
|
||||
@@ -609,21 +611,18 @@ class SpotiFLACGUI(QWidget):
|
||||
file_layout.setSpacing(5)
|
||||
|
||||
file_label = QLabel('File Settings')
|
||||
file_label.setStyleSheet("font-weight: bold; color: palette(text);")
|
||||
file_label.setStyleSheet("font-weight: bold;")
|
||||
file_layout.addWidget(file_label)
|
||||
|
||||
format_layout = QHBoxLayout()
|
||||
format_label = QLabel('Filename Format:')
|
||||
format_label.setStyleSheet("color: palette(text);")
|
||||
|
||||
self.format_group = QButtonGroup(self)
|
||||
self.title_artist_radio = QRadioButton('Title - Artist')
|
||||
self.title_artist_radio.setStyleSheet("color: palette(text);")
|
||||
self.title_artist_radio.setCursor(Qt.CursorShape.PointingHandCursor)
|
||||
self.title_artist_radio.toggled.connect(self.save_filename_format)
|
||||
|
||||
self.artist_title_radio = QRadioButton('Artist - Title')
|
||||
self.artist_title_radio.setStyleSheet("color: palette(text);")
|
||||
self.artist_title_radio.setCursor(Qt.CursorShape.PointingHandCursor)
|
||||
self.artist_title_radio.toggled.connect(self.save_filename_format)
|
||||
|
||||
@@ -644,14 +643,12 @@ class SpotiFLACGUI(QWidget):
|
||||
checkbox_layout = QHBoxLayout()
|
||||
|
||||
self.track_number_checkbox = QCheckBox('Add Track Numbers to Album Files')
|
||||
self.track_number_checkbox.setStyleSheet("color: palette(text);")
|
||||
self.track_number_checkbox.setCursor(Qt.CursorShape.PointingHandCursor)
|
||||
self.track_number_checkbox.setChecked(self.use_track_numbers)
|
||||
self.track_number_checkbox.toggled.connect(self.save_track_numbering)
|
||||
checkbox_layout.addWidget(self.track_number_checkbox)
|
||||
|
||||
self.album_subfolder_checkbox = QCheckBox('Create Album Subfolders for Playlist Downloads')
|
||||
self.album_subfolder_checkbox.setStyleSheet("color: palette(text);")
|
||||
self.album_subfolder_checkbox.setCursor(Qt.CursorShape.PointingHandCursor)
|
||||
self.album_subfolder_checkbox.setChecked(self.use_album_subfolders)
|
||||
self.album_subfolder_checkbox.toggled.connect(self.save_album_subfolder_setting)
|
||||
@@ -666,14 +663,13 @@ class SpotiFLACGUI(QWidget):
|
||||
auth_layout = QVBoxLayout(auth_group)
|
||||
auth_layout.setSpacing(5)
|
||||
|
||||
auth_label = QLabel('Lucida')
|
||||
auth_label.setStyleSheet("font-weight: bold; color: palette(text);")
|
||||
auth_label = QLabel('Lucida Settings')
|
||||
auth_label.setStyleSheet("font-weight: bold;")
|
||||
auth_layout.addWidget(auth_label)
|
||||
|
||||
service_fallback_layout = QHBoxLayout()
|
||||
|
||||
service_label = QLabel('Service:')
|
||||
service_label.setStyleSheet("color: palette(text);")
|
||||
|
||||
self.service_dropdown = ServiceComboBox()
|
||||
self.service_dropdown.currentIndexChanged.connect(self.save_service_setting)
|
||||
@@ -684,12 +680,21 @@ class SpotiFLACGUI(QWidget):
|
||||
service_fallback_layout.addSpacing(20)
|
||||
|
||||
self.fallback_checkbox = QCheckBox('Fallback')
|
||||
self.fallback_checkbox.setStyleSheet("color: palette(text);")
|
||||
self.fallback_checkbox.setCursor(Qt.CursorShape.PointingHandCursor)
|
||||
self.fallback_checkbox.setChecked(self.use_fallback)
|
||||
self.fallback_checkbox.toggled.connect(self.save_fallback_setting)
|
||||
service_fallback_layout.addWidget(self.fallback_checkbox)
|
||||
|
||||
service_fallback_layout.addSpacing(20)
|
||||
|
||||
timeout_label = QLabel('Timeout:')
|
||||
self.timeout_input = QLineEdit()
|
||||
self.timeout_input.setText(str(self.timeout_value))
|
||||
self.timeout_input.setFixedWidth(60)
|
||||
self.timeout_input.textChanged.connect(self.save_timeout_setting)
|
||||
service_fallback_layout.addWidget(timeout_label)
|
||||
service_fallback_layout.addWidget(self.timeout_input)
|
||||
|
||||
service_fallback_layout.addStretch()
|
||||
auth_layout.addLayout(service_fallback_layout)
|
||||
|
||||
@@ -749,8 +754,8 @@ class SpotiFLACGUI(QWidget):
|
||||
spacer = QSpacerItem(20, 6, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Fixed)
|
||||
about_layout.addItem(spacer)
|
||||
|
||||
footer_label = QLabel("v2.4 | April 2025")
|
||||
footer_label.setStyleSheet("font-size: 12px; color: palette(text); margin-top: 10px;")
|
||||
footer_label = QLabel("v2.6 | May 2025")
|
||||
footer_label.setStyleSheet("font-size: 12px; margin-top: 10px;")
|
||||
about_layout.addWidget(footer_label, alignment=Qt.AlignmentFlag.AlignCenter)
|
||||
|
||||
about_tab.setLayout(about_layout)
|
||||
@@ -781,6 +786,21 @@ class SpotiFLACGUI(QWidget):
|
||||
self.settings.sync()
|
||||
self.log_output.append("Fallback setting saved successfully!")
|
||||
|
||||
def save_timeout_setting(self):
|
||||
try:
|
||||
timeout = int(self.timeout_input.text())
|
||||
if timeout > 0:
|
||||
self.timeout_value = timeout
|
||||
self.settings.setValue('timeout_value', self.timeout_value)
|
||||
self.settings.sync()
|
||||
self.log_output.append(f"Timeout setting saved: {self.timeout_value} seconds")
|
||||
else:
|
||||
self.timeout_input.setText(str(self.timeout_value))
|
||||
self.log_output.append("Timeout must be a positive number")
|
||||
except ValueError:
|
||||
self.timeout_input.setText(str(self.timeout_value))
|
||||
self.log_output.append("Timeout must be a valid number")
|
||||
|
||||
def save_service_setting(self):
|
||||
service = self.service_dropdown.currentData()
|
||||
self.service = service
|
||||
@@ -950,10 +970,21 @@ class SpotiFLACGUI(QWidget):
|
||||
self.followers_label.hide()
|
||||
|
||||
if metadata.get('releaseDate'):
|
||||
release_date = datetime.strptime(metadata['releaseDate'], "%Y-%m-%d")
|
||||
formatted_date = release_date.strftime("%d-%m-%Y")
|
||||
try:
|
||||
release_date = metadata['releaseDate']
|
||||
if len(release_date) == 4:
|
||||
date_obj = datetime.strptime(release_date, "%Y")
|
||||
elif len(release_date) == 7:
|
||||
date_obj = datetime.strptime(release_date, "%Y-%m")
|
||||
else:
|
||||
date_obj = datetime.strptime(release_date, "%Y-%m-%d")
|
||||
|
||||
formatted_date = date_obj.strftime("%d-%m-%Y")
|
||||
self.release_date_label.setText(f"<b>Released</b> {formatted_date}")
|
||||
self.release_date_label.show()
|
||||
except ValueError:
|
||||
self.release_date_label.setText(f"<b>Released</b> {metadata['releaseDate']}")
|
||||
self.release_date_label.show()
|
||||
else:
|
||||
self.release_date_label.hide()
|
||||
|
||||
@@ -1064,7 +1095,8 @@ class SpotiFLACGUI(QWidget):
|
||||
self.use_track_numbers,
|
||||
self.use_album_subfolders,
|
||||
self.use_fallback,
|
||||
service
|
||||
service,
|
||||
self.timeout_value
|
||||
)
|
||||
self.worker.finished.connect(self.on_download_finished)
|
||||
self.worker.progress.connect(self.update_progress)
|
||||
|
||||
+4
-3
@@ -6,13 +6,14 @@ import re
|
||||
import base64
|
||||
|
||||
class TrackDownloader:
|
||||
def __init__(self, use_fallback=False):
|
||||
def __init__(self, use_fallback=False, timeout=30):
|
||||
self.client = requests.Session()
|
||||
self.headers = {
|
||||
'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.use_fallback = use_fallback
|
||||
self.timeout = timeout
|
||||
self.base_domain = "lucida.su" if use_fallback else "lucida.to"
|
||||
|
||||
def set_progress_callback(self, callback):
|
||||
@@ -73,7 +74,7 @@ class TrackDownloader:
|
||||
base_url,
|
||||
params=request_params,
|
||||
headers=headers,
|
||||
timeout=30
|
||||
timeout=self.timeout
|
||||
)
|
||||
|
||||
html_content = response.text
|
||||
@@ -294,7 +295,7 @@ async def main():
|
||||
|
||||
output_dir = "."
|
||||
track_id = "2plbrEY59IikOBgBGLjaoe"
|
||||
service = "amazon"
|
||||
service = "tidal"
|
||||
|
||||
def progress_update(current, total):
|
||||
if total > 0:
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"version": "2.3"
|
||||
"version": "2.5"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user