Implemented quality select

This commit is contained in:
cool.gitter.choco
2025-01-29 12:40:26 -06:00
parent 11d769aac7
commit a89a236f46
9 changed files with 542 additions and 545 deletions

View File

@@ -27,7 +27,7 @@ class FlushingFileWrapper:
def flush(self): def flush(self):
self.file.flush() self.file.flush()
def download_task(service, url, main, fallback, prg_path): def download_task(service, url, main, fallback, quality, fall_quality, prg_path):
try: try:
from routes.utils.album import download_album from routes.utils.album import download_album
with open(prg_path, 'w') as f: with open(prg_path, 'w') as f:
@@ -40,7 +40,9 @@ def download_task(service, url, main, fallback, prg_path):
service=service, service=service,
url=url, url=url,
main=main, main=main,
fallback=fallback fallback=fallback,
quality=quality,
fall_quality=fall_quality
) )
flushing_file.write(json.dumps({"status": "complete"}) + "\n") flushing_file.write(json.dumps({"status": "complete"}) + "\n")
except Exception as e: except Exception as e:
@@ -67,6 +69,14 @@ def handle_download():
url = request.args.get('url') url = request.args.get('url')
main = request.args.get('main') main = request.args.get('main')
fallback = request.args.get('fallback') fallback = request.args.get('fallback')
quality = request.args.get('quality')
fall_quality = request.args.get('fall_quality')
# Sanitize main and fallback to prevent directory traversal
if main:
main = os.path.basename(main)
if fallback:
fallback = os.path.basename(fallback)
if not all([service, url, main]): if not all([service, url, main]):
return Response( return Response(
@@ -75,6 +85,56 @@ def handle_download():
mimetype='application/json' mimetype='application/json'
) )
# Validate credentials based on service and fallback
try:
if service == 'spotify':
if fallback:
# Validate Deezer main and Spotify fallback credentials
deezer_creds_path = os.path.abspath(os.path.join('./creds/deezer', main, 'credentials.json'))
if not os.path.isfile(deezer_creds_path):
return Response(
json.dumps({"error": "Invalid Deezer credentials directory"}),
status=400,
mimetype='application/json'
)
spotify_fallback_path = os.path.abspath(os.path.join('./creds/spotify', fallback, 'credentials.json'))
if not os.path.isfile(spotify_fallback_path):
return Response(
json.dumps({"error": "Invalid Spotify fallback credentials directory"}),
status=400,
mimetype='application/json'
)
else:
# Validate Spotify main credentials
spotify_creds_path = os.path.abspath(os.path.join('./creds/spotify', main, 'credentials.json'))
if not os.path.isfile(spotify_creds_path):
return Response(
json.dumps({"error": "Invalid Spotify credentials directory"}),
status=400,
mimetype='application/json'
)
elif service == 'deezer':
# Validate Deezer main credentials
deezer_creds_path = os.path.abspath(os.path.join('./creds/deezer', main, 'credentials.json'))
if not os.path.isfile(deezer_creds_path):
return Response(
json.dumps({"error": "Invalid Deezer credentials directory"}),
status=400,
mimetype='application/json'
)
else:
return Response(
json.dumps({"error": "Unsupported service"}),
status=400,
mimetype='application/json'
)
except Exception as e:
return Response(
json.dumps({"error": f"Credential validation failed: {str(e)}"}),
status=500,
mimetype='application/json'
)
filename = generate_random_filename() filename = generate_random_filename()
prg_dir = './prgs' prg_dir = './prgs'
os.makedirs(prg_dir, exist_ok=True) os.makedirs(prg_dir, exist_ok=True)
@@ -82,7 +142,7 @@ def handle_download():
Process( Process(
target=download_task, target=download_task,
args=(service, url, main, fallback, prg_path) args=(service, url, main, fallback, quality, fall_quality, prg_path)
).start() ).start()
return Response( return Response(

View File

@@ -26,7 +26,7 @@ class FlushingFileWrapper:
def flush(self): def flush(self):
self.file.flush() self.file.flush()
def download_task(service, url, main, fallback, prg_path): def download_task(service, url, main, fallback, quality, fall_quality, prg_path):
try: try:
from routes.utils.playlist import download_playlist from routes.utils.playlist import download_playlist
with open(prg_path, 'w') as f: with open(prg_path, 'w') as f:
@@ -39,7 +39,9 @@ def download_task(service, url, main, fallback, prg_path):
service=service, service=service,
url=url, url=url,
main=main, main=main,
fallback=fallback fallback=fallback,
quality=quality,
fall_quality=fall_quality
) )
flushing_file.write(json.dumps({"status": "complete"}) + "\n") flushing_file.write(json.dumps({"status": "complete"}) + "\n")
except Exception as e: except Exception as e:
@@ -66,6 +68,8 @@ def handle_download():
url = request.args.get('url') url = request.args.get('url')
main = request.args.get('main') main = request.args.get('main')
fallback = request.args.get('fallback') fallback = request.args.get('fallback')
quality = request.args.get('quality')
fall_quality = request.args.get('fall_quality')
if not all([service, url, main]): if not all([service, url, main]):
return Response( return Response(
@@ -81,7 +85,7 @@ def handle_download():
Process( Process(
target=download_task, target=download_task,
args=(service, url, main, fallback, prg_path) args=(service, url, main, fallback, quality, fall_quality, prg_path)
).start() ).start()
return Response( return Response(

View File

@@ -26,7 +26,7 @@ class FlushingFileWrapper:
def flush(self): def flush(self):
self.file.flush() self.file.flush()
def download_task(service, url, main, fallback, prg_path): def download_task(service, url, main, fallback, quality, fall_quality, prg_path):
try: try:
from routes.utils.track import download_track from routes.utils.track import download_track
with open(prg_path, 'w') as f: with open(prg_path, 'w') as f:
@@ -39,7 +39,9 @@ def download_task(service, url, main, fallback, prg_path):
service=service, service=service,
url=url, url=url,
main=main, main=main,
fallback=fallback fallback=fallback,
quality=quality,
fall_quality=fall_quality
) )
flushing_file.write(json.dumps({"status": "complete"}) + "\n") flushing_file.write(json.dumps({"status": "complete"}) + "\n")
except Exception as e: except Exception as e:
@@ -66,6 +68,8 @@ def handle_download():
url = request.args.get('url') url = request.args.get('url')
main = request.args.get('main') main = request.args.get('main')
fallback = request.args.get('fallback') fallback = request.args.get('fallback')
quality = request.args.get('quality')
fall_quality = request.args.get('fall_quality')
if not all([service, url, main]): if not all([service, url, main]):
return Response( return Response(
@@ -81,7 +85,7 @@ def handle_download():
Process( Process(
target=download_task, target=download_task,
args=(service, url, main, fallback, prg_path) args=(service, url, main, fallback, quality, fall_quality, prg_path)
).start() ).start()
return Response( return Response(

View File

@@ -4,10 +4,15 @@ import traceback
from deezspot.spotloader import SpoLogin from deezspot.spotloader import SpoLogin
from deezspot.deezloader import DeeLogin from deezspot.deezloader import DeeLogin
def download_album(service, url, main, fallback=None): def download_album(service, url, main, fallback=None, quality=None, fall_quality=None):
try: try:
if service == 'spotify': if service == 'spotify':
if fallback: if fallback:
if quality is None:
quality = 'FLAC'
if fall_quality is None:
fall_quality='HIGH'
# First attempt: use DeeLogin's download_albumspo with the 'main' (Deezer credentials) # First attempt: use DeeLogin's download_albumspo with the 'main' (Deezer credentials)
try: try:
# Load Deezer credentials from 'main' under deezer directory # Load Deezer credentials from 'main' under deezer directory
@@ -23,7 +28,7 @@ def download_album(service, url, main, fallback=None):
dl.download_albumspo( dl.download_albumspo(
link_album=url, link_album=url,
output_dir="./downloads", output_dir="./downloads",
quality_download="FLAC", quality_download=quality,
recursive_quality=True, recursive_quality=True,
recursive_download=False, recursive_download=False,
not_interface=False, not_interface=False,
@@ -39,7 +44,7 @@ def download_album(service, url, main, fallback=None):
spo.download_album( spo.download_album(
link_album=url, link_album=url,
output_dir="./downloads", output_dir="./downloads",
quality_download="HIGH", quality_download=fall_quality,
recursive_quality=True, recursive_quality=True,
recursive_download=False, recursive_download=False,
not_interface=False, not_interface=False,
@@ -54,13 +59,15 @@ def download_album(service, url, main, fallback=None):
) from e2 ) from e2
else: else:
# Original behavior: use Spotify main # Original behavior: use Spotify main
if quality is None:
quality ='HIGH'
creds_dir = os.path.join('./creds/spotify', main) creds_dir = os.path.join('./creds/spotify', main)
credentials_path = os.path.abspath(os.path.join(creds_dir, 'credentials.json')) credentials_path = os.path.abspath(os.path.join(creds_dir, 'credentials.json'))
spo = SpoLogin(credentials_path=credentials_path) spo = SpoLogin(credentials_path=credentials_path)
spo.download_album( spo.download_album(
link_album=url, link_album=url,
output_dir="./downloads", output_dir="./downloads",
quality_download="HIGH", quality_download=quality,
recursive_quality=True, recursive_quality=True,
recursive_download=False, recursive_download=False,
not_interface=False, not_interface=False,
@@ -68,6 +75,8 @@ def download_album(service, url, main, fallback=None):
make_zip=False make_zip=False
) )
elif service == 'deezer': elif service == 'deezer':
if quality is None:
quality='FLAC'
# Existing code remains the same, ignoring fallback # Existing code remains the same, ignoring fallback
creds_dir = os.path.join('./creds/deezer', main) creds_dir = os.path.join('./creds/deezer', main)
creds_path = os.path.abspath(os.path.join(creds_dir, 'credentials.json')) creds_path = os.path.abspath(os.path.join(creds_dir, 'credentials.json'))
@@ -79,7 +88,7 @@ def download_album(service, url, main, fallback=None):
dl.download_albumdee( dl.download_albumdee(
link_album=url, link_album=url,
output_dir="./downloads", output_dir="./downloads",
quality_download="FLAC", quality_download=quality,
recursive_quality=True, recursive_quality=True,
recursive_download=False, recursive_download=False,
method_save=1, method_save=1,

View File

@@ -4,10 +4,15 @@ import traceback
from deezspot.spotloader import SpoLogin from deezspot.spotloader import SpoLogin
from deezspot.deezloader import DeeLogin from deezspot.deezloader import DeeLogin
def download_playlist(service, url, main, fallback=None): def download_playlist(service, url, main, fallback=None, quality=None, fall_quality=None):
try: try:
if service == 'spotify': if service == 'spotify':
if fallback: if fallback:
if quality is None:
quality = 'FLAC'
if fall_quality is None:
fall_quality='HIGH'
# First attempt: use DeeLogin's download_playlistspo with the 'main' (Deezer credentials) # First attempt: use DeeLogin's download_playlistspo with the 'main' (Deezer credentials)
try: try:
# Load Deezer credentials from 'main' under deezer directory # Load Deezer credentials from 'main' under deezer directory
@@ -23,7 +28,7 @@ def download_playlist(service, url, main, fallback=None):
dl.download_playlistspo( dl.download_playlistspo(
link_playlist=url, link_playlist=url,
output_dir="./downloads", output_dir="./downloads",
quality_download="FLAC", quality_download=quality,
recursive_quality=True, recursive_quality=True,
recursive_download=False, recursive_download=False,
not_interface=False, not_interface=False,
@@ -39,7 +44,7 @@ def download_playlist(service, url, main, fallback=None):
spo.download_playlist( spo.download_playlist(
link_playlist=url, link_playlist=url,
output_dir="./downloads", output_dir="./downloads",
quality_download="HIGH", quality_download=fall_quality,
recursive_quality=True, recursive_quality=True,
recursive_download=False, recursive_download=False,
not_interface=False, not_interface=False,
@@ -54,13 +59,15 @@ def download_playlist(service, url, main, fallback=None):
) from e2 ) from e2
else: else:
# Original behavior: use Spotify main # Original behavior: use Spotify main
if quality is None:
quality='HIGH'
creds_dir = os.path.join('./creds/spotify', main) creds_dir = os.path.join('./creds/spotify', main)
credentials_path = os.path.abspath(os.path.join(creds_dir, 'credentials.json')) credentials_path = os.path.abspath(os.path.join(creds_dir, 'credentials.json'))
spo = SpoLogin(credentials_path=credentials_path) spo = SpoLogin(credentials_path=credentials_path)
spo.download_playlist( spo.download_playlist(
link_playlist=url, link_playlist=url,
output_dir="./downloads", output_dir="./downloads",
quality_download="HIGH", quality_download=quality,
recursive_quality=True, recursive_quality=True,
recursive_download=False, recursive_download=False,
not_interface=False, not_interface=False,
@@ -68,6 +75,8 @@ def download_playlist(service, url, main, fallback=None):
make_zip=False make_zip=False
) )
elif service == 'deezer': elif service == 'deezer':
if quality is None:
quality='FLAC'
# Existing code for Deezer, using main as Deezer account # Existing code for Deezer, using main as Deezer account
creds_dir = os.path.join('./creds/deezer', main) creds_dir = os.path.join('./creds/deezer', main)
creds_path = os.path.abspath(os.path.join(creds_dir, 'credentials.json')) creds_path = os.path.abspath(os.path.join(creds_dir, 'credentials.json'))
@@ -79,7 +88,7 @@ def download_playlist(service, url, main, fallback=None):
dl.download_playlistdee( dl.download_playlistdee(
link_playlist=url, link_playlist=url,
output_dir="./downloads", output_dir="./downloads",
quality_download="FLAC", quality_download=quality,
recursive_quality=False, recursive_quality=False,
recursive_download=False, recursive_download=False,
method_save=1, method_save=1,

View File

@@ -4,10 +4,15 @@ import traceback
from deezspot.spotloader import SpoLogin from deezspot.spotloader import SpoLogin
from deezspot.deezloader import DeeLogin from deezspot.deezloader import DeeLogin
def download_track(service, url, main, fallback=None): def download_track(service, url, main, fallback=None, quality=None, fall_quality=None):
try: try:
if service == 'spotify': if service == 'spotify':
if fallback: if fallback:
if quality is None:
quality = 'FLAC'
if fall_quality is None:
fall_quality='HIGH'
# First attempt: use Deezer's download_trackspo with 'main' (Deezer credentials) # First attempt: use Deezer's download_trackspo with 'main' (Deezer credentials)
try: try:
deezer_creds_dir = os.path.join('./creds/deezer', main) deezer_creds_dir = os.path.join('./creds/deezer', main)
@@ -20,7 +25,7 @@ def download_track(service, url, main, fallback=None):
dl.download_trackspo( dl.download_trackspo(
link_track=url, link_track=url,
output_dir="./downloads", output_dir="./downloads",
quality_download="FLAC", quality_download=quality,
recursive_quality=False, recursive_quality=False,
recursive_download=False, recursive_download=False,
not_interface=False, not_interface=False,
@@ -33,7 +38,7 @@ def download_track(service, url, main, fallback=None):
spo.download_track( spo.download_track(
link_track=url, link_track=url,
output_dir="./downloads", output_dir="./downloads",
quality_download="HIGH", quality_download=fall_quality,
recursive_quality=False, recursive_quality=False,
recursive_download=False, recursive_download=False,
not_interface=False, not_interface=False,
@@ -41,19 +46,23 @@ def download_track(service, url, main, fallback=None):
) )
else: else:
# Directly use Spotify main account # Directly use Spotify main account
if quality is None:
quality='HIGH'
creds_dir = os.path.join('./creds/spotify', main) creds_dir = os.path.join('./creds/spotify', main)
credentials_path = os.path.abspath(os.path.join(creds_dir, 'credentials.json')) credentials_path = os.path.abspath(os.path.join(creds_dir, 'credentials.json'))
spo = SpoLogin(credentials_path=credentials_path) spo = SpoLogin(credentials_path=credentials_path)
spo.download_track( spo.download_track(
link_track=url, link_track=url,
output_dir="./downloads", output_dir="./downloads",
quality_download="HIGH", quality_download=quality,
recursive_quality=False, recursive_quality=False,
recursive_download=False, recursive_download=False,
not_interface=False, not_interface=False,
method_save=1 method_save=1
) )
elif service == 'deezer': elif service == 'deezer':
if quality is None:
quality='FLAC'
# Deezer download logic remains unchanged # Deezer download logic remains unchanged
creds_dir = os.path.join('./creds/deezer', main) creds_dir = os.path.join('./creds/deezer', main)
creds_path = os.path.abspath(os.path.join(creds_dir, 'credentials.json')) creds_path = os.path.abspath(os.path.join(creds_dir, 'credentials.json'))
@@ -65,7 +74,7 @@ def download_track(service, url, main, fallback=None):
dl.download_trackdee( dl.download_trackdee(
link_track=url, link_track=url,
output_dir="./downloads", output_dir="./downloads",
quality_download="FLAC", quality_download=quality,
recursive_quality=False, recursive_quality=False,
recursive_download=False, recursive_download=False,
method_save=1 method_save=1

File diff suppressed because it is too large Load Diff

View File

@@ -76,23 +76,40 @@ document.addEventListener('DOMContentLoaded', () => {
async function initConfig() { async function initConfig() {
loadConfig(); loadConfig();
console.log(loadConfig())
await updateAccountSelectors(); await updateAccountSelectors();
// Event listeners // Existing listeners
document.getElementById('fallbackToggle').addEventListener('change', () => { const fallbackToggle = document.getElementById('fallbackToggle');
saveConfig(); if (fallbackToggle) {
updateAccountSelectors(); fallbackToggle.addEventListener('change', () => {
});
const accountSelects = ['spotifyAccountSelect', 'deezerAccountSelect'];
accountSelects.forEach(id => {
document.getElementById(id).addEventListener('change', () => {
saveConfig(); saveConfig();
updateAccountSelectors(); updateAccountSelectors();
}); });
}
const accountSelects = ['spotifyAccountSelect', 'deezerAccountSelect'];
accountSelects.forEach(id => {
const element = document.getElementById(id);
if (element) {
element.addEventListener('change', () => {
saveConfig();
updateAccountSelectors();
});
}
}); });
// Add quality select listeners with null checks
const spotifyQuality = document.getElementById('spotifyQualitySelect');
if (spotifyQuality) {
spotifyQuality.addEventListener('change', saveConfig);
}
const deezerQuality = document.getElementById('deezerQualitySelect');
if (deezerQuality) {
deezerQuality.addEventListener('change', saveConfig);
}
} }
async function updateAccountSelectors() { async function updateAccountSelectors() {
try { try {
@@ -273,23 +290,43 @@ async function startDownload(url, type, item) {
const spotifyAccount = document.getElementById('spotifyAccountSelect').value; const spotifyAccount = document.getElementById('spotifyAccountSelect').value;
const deezerAccount = document.getElementById('deezerAccountSelect').value; const deezerAccount = document.getElementById('deezerAccountSelect').value;
let apiUrl = `/api/${type}/download?service=spotify&url=${encodeURIComponent(url)}`; // Determine service from URL
let service;
if (fallbackEnabled) { if (url.includes('open.spotify.com')) {
apiUrl += `&main=${deezerAccount}&fallback=${spotifyAccount}`; service = 'spotify';
} else if (url.includes('deezer.com')) {
service = 'deezer';
} else { } else {
apiUrl += `&main=${spotifyAccount}`; showError('Unsupported service URL');
return;
} }
let apiUrl = `/api/${type}/download?service=${service}&url=${encodeURIComponent(url)}`;
// Get quality settings
const spotifyQuality = document.getElementById('spotifyQualitySelect').value;
const deezerQuality = document.getElementById('deezerQualitySelect').value;
if (fallbackEnabled && service === 'spotify') {
// Deezer fallback for Spotify URLs
apiUrl += `&main=${deezerAccount}&fallback=${spotifyAccount}`;
apiUrl += `&quality=${encodeURIComponent(deezerQuality)}`;
apiUrl += `&fall_quality=${encodeURIComponent(spotifyQuality)}`;
} else {
// Standard download without fallback
const mainAccount = service === 'spotify' ? spotifyAccount : deezerAccount;
apiUrl += `&main=${mainAccount}`;
apiUrl += `&quality=${encodeURIComponent(service === 'spotify' ? spotifyQuality : deezerQuality)}`;
}
try { try {
const response = await fetch(apiUrl); const response = await fetch(apiUrl);
const data = await response.json(); const data = await response.json();
addToQueue(item, type, data.prg_file);
addToQueue(item, type, data.prg_file);
} catch (error) { } catch (error) {
showError('Download failed: ' + error.message); showError('Download failed: ' + error.message);
} }
} }
function addToQueue(item, type, prgFile) { function addToQueue(item, type, prgFile) {
const queueId = Date.now().toString() + Math.random().toString(36).substr(2, 9); const queueId = Date.now().toString() + Math.random().toString(36).substr(2, 9);
@@ -630,9 +667,11 @@ function getStatusMessage(data) {
function saveConfig() { function saveConfig() {
const config = { const config = {
spotify: document.getElementById('spotifyAccountSelect').value, spotify: document.getElementById('spotifyAccountSelect').value,
deezer: document.getElementById('deezerAccountSelect').value, deezer: document.getElementById('deezerAccountSelect').value,
fallback: document.getElementById('fallbackToggle').checked fallback: document.getElementById('fallbackToggle').checked,
spotifyQuality: document.getElementById('spotifyQualitySelect').value,
deezerQuality: document.getElementById('deezerQualitySelect').value
}; };
localStorage.setItem('activeConfig', JSON.stringify(config)); localStorage.setItem('activeConfig', JSON.stringify(config));
} }
@@ -641,16 +680,24 @@ function saveConfig() {
function loadConfig() { function loadConfig() {
const saved = JSON.parse(localStorage.getItem('activeConfig')) || {}; const saved = JSON.parse(localStorage.getItem('activeConfig')) || {};
// Set values only if they exist in the DOM // Account selects
const spotifySelect = document.getElementById('spotifyAccountSelect'); const spotifySelect = document.getElementById('spotifyAccountSelect');
const deezerSelect = document.getElementById('deezerAccountSelect');
if (spotifySelect) spotifySelect.value = saved.spotify || ''; if (spotifySelect) spotifySelect.value = saved.spotify || '';
const deezerSelect = document.getElementById('deezerAccountSelect');
if (deezerSelect) deezerSelect.value = saved.deezer || ''; if (deezerSelect) deezerSelect.value = saved.deezer || '';
// Fallback toggle
const fallbackToggle = document.getElementById('fallbackToggle'); const fallbackToggle = document.getElementById('fallbackToggle');
if (fallbackToggle) fallbackToggle.checked = !!saved.fallback; if (fallbackToggle) fallbackToggle.checked = !!saved.fallback;
}
// Quality selects
const spotifyQuality = document.getElementById('spotifyQualitySelect');
if (spotifyQuality) spotifyQuality.value = saved.spotifyQuality || 'NORMAL';
const deezerQuality = document.getElementById('deezerQualitySelect');
if (deezerQuality) deezerQuality.value = saved.deezerQuality || 'MP3_128';
}
function isSpotifyUrl(url) { function isSpotifyUrl(url) {
return url.startsWith('https://open.spotify.com/'); return url.startsWith('https://open.spotify.com/');

View File

@@ -19,10 +19,26 @@
<label>Active Spotify Account:</label> <label>Active Spotify Account:</label>
<select id="spotifyAccountSelect"></select> <select id="spotifyAccountSelect"></select>
</div> </div>
<div class="config-item">
<label>Spotify Quality:</label>
<select id="spotifyQualitySelect">
<option value="NORMAL">Normal</option>
<option value="HIGH">High</option>
<option value="VERY_HIGH">Very High</option>
</select>
</div>
<div class="config-item"> <div class="config-item">
<label>Active Deezer Account:</label> <label>Active Deezer Account:</label>
<select id="deezerAccountSelect"></select> <select id="deezerAccountSelect"></select>
</div> </div>
<div class="config-item">
<label>Deezer Quality:</label>
<select id="deezerQualitySelect">
<option value="MP3_128">MP3 128</option>
<option value="MP3_320">MP3 320</option>
<option value="FLAC">FLAC</option>
</select>
</div>
<div class="config-item"> <div class="config-item">
<label>Download Fallback:</label> <label>Download Fallback:</label>
<label class="switch"> <label class="switch">