added endpoints for custom dir and track format
This commit is contained in:
@@ -40,7 +40,8 @@ class FlushingFileWrapper:
|
|||||||
def flush(self):
|
def flush(self):
|
||||||
self.file.flush()
|
self.file.flush()
|
||||||
|
|
||||||
def download_task(service, url, main, fallback, quality, fall_quality, real_time, prg_path, orig_request):
|
def download_task(service, url, main, fallback, quality, fall_quality, real_time, prg_path, orig_request,
|
||||||
|
custom_dir_format, custom_track_format):
|
||||||
"""
|
"""
|
||||||
The download task writes out the original request data into the progress file
|
The download task writes out the original request data into the progress file
|
||||||
and then runs the album download.
|
and then runs the album download.
|
||||||
@@ -69,7 +70,9 @@ def download_task(service, url, main, fallback, quality, fall_quality, real_time
|
|||||||
fallback=fallback,
|
fallback=fallback,
|
||||||
quality=quality,
|
quality=quality,
|
||||||
fall_quality=fall_quality,
|
fall_quality=fall_quality,
|
||||||
real_time=real_time
|
real_time=real_time,
|
||||||
|
custom_dir_format=custom_dir_format,
|
||||||
|
custom_track_format=custom_track_format
|
||||||
)
|
)
|
||||||
flushing_file.write(json.dumps({"status": "complete"}) + "\n")
|
flushing_file.write(json.dumps({"status": "complete"}) + "\n")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -103,6 +106,10 @@ def handle_download():
|
|||||||
real_time_arg = request.args.get('real_time', 'false')
|
real_time_arg = request.args.get('real_time', 'false')
|
||||||
real_time = real_time_arg.lower() in ['true', '1', 'yes']
|
real_time = real_time_arg.lower() in ['true', '1', 'yes']
|
||||||
|
|
||||||
|
# New custom formatting parameters (with defaults)
|
||||||
|
custom_dir_format = request.args.get('custom_dir_format', "%ar_album%/%album%")
|
||||||
|
custom_track_format = request.args.get('custom_track_format', "%tracknum%. %music% - %artist%")
|
||||||
|
|
||||||
# Sanitize main and fallback to prevent directory traversal
|
# Sanitize main and fallback to prevent directory traversal
|
||||||
if main:
|
if main:
|
||||||
main = os.path.basename(main)
|
main = os.path.basename(main)
|
||||||
@@ -177,7 +184,19 @@ def handle_download():
|
|||||||
# Create and start the download process, and track it in the global dictionary.
|
# Create and start the download process, and track it in the global dictionary.
|
||||||
process = Process(
|
process = Process(
|
||||||
target=download_task,
|
target=download_task,
|
||||||
args=(service, url, main, fallback, quality, fall_quality, real_time, prg_path, orig_request)
|
args=(
|
||||||
|
service,
|
||||||
|
url,
|
||||||
|
main,
|
||||||
|
fallback,
|
||||||
|
quality,
|
||||||
|
fall_quality,
|
||||||
|
real_time,
|
||||||
|
prg_path,
|
||||||
|
orig_request,
|
||||||
|
custom_dir_format,
|
||||||
|
custom_track_format
|
||||||
|
)
|
||||||
)
|
)
|
||||||
process.start()
|
process.start()
|
||||||
download_processes[filename] = process
|
download_processes[filename] = process
|
||||||
|
|||||||
@@ -44,7 +44,8 @@ class FlushingFileWrapper:
|
|||||||
def flush(self):
|
def flush(self):
|
||||||
self.file.flush()
|
self.file.flush()
|
||||||
|
|
||||||
def download_artist_task(service, artist_url, main, fallback, quality, fall_quality, real_time, album_type, prg_path, orig_request):
|
def download_artist_task(service, artist_url, main, fallback, quality, fall_quality, real_time,
|
||||||
|
album_type, prg_path, orig_request, custom_dir_format, custom_track_format):
|
||||||
"""
|
"""
|
||||||
This function wraps the call to download_artist_albums, writes the original
|
This function wraps the call to download_artist_albums, writes the original
|
||||||
request data to the progress file, and then writes JSON status updates.
|
request data to the progress file, and then writes JSON status updates.
|
||||||
@@ -75,6 +76,8 @@ def download_artist_task(service, artist_url, main, fallback, quality, fall_qual
|
|||||||
fall_quality=fall_quality,
|
fall_quality=fall_quality,
|
||||||
real_time=real_time,
|
real_time=real_time,
|
||||||
album_type=album_type,
|
album_type=album_type,
|
||||||
|
custom_dir_format=custom_dir_format,
|
||||||
|
custom_track_format=custom_track_format
|
||||||
)
|
)
|
||||||
flushing_file.write(json.dumps({"status": "complete"}) + "\n")
|
flushing_file.write(json.dumps({"status": "complete"}) + "\n")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -108,6 +111,8 @@ def handle_artist_download():
|
|||||||
- fall_quality: string (optional, e.g., "HIGH")
|
- fall_quality: string (optional, e.g., "HIGH")
|
||||||
- real_time: bool (e.g., "true" or "false")
|
- real_time: bool (e.g., "true" or "false")
|
||||||
- album_type: string(s); one or more of "album", "single", "appears_on", "compilation" (if multiple, comma-separated)
|
- album_type: string(s); one or more of "album", "single", "appears_on", "compilation" (if multiple, comma-separated)
|
||||||
|
- custom_dir_format: string (optional, default: "%ar_album%/%album%/%copyright%")
|
||||||
|
- custom_track_format: string (optional, default: "%tracknum%. %music% - %artist%")
|
||||||
"""
|
"""
|
||||||
service = request.args.get('service')
|
service = request.args.get('service')
|
||||||
artist_url = request.args.get('artist_url')
|
artist_url = request.args.get('artist_url')
|
||||||
@@ -119,6 +124,10 @@ def handle_artist_download():
|
|||||||
real_time_arg = request.args.get('real_time', 'false')
|
real_time_arg = request.args.get('real_time', 'false')
|
||||||
real_time = real_time_arg.lower() in ['true', '1', 'yes']
|
real_time = real_time_arg.lower() in ['true', '1', 'yes']
|
||||||
|
|
||||||
|
# New query parameters for custom formatting.
|
||||||
|
custom_dir_format = request.args.get('custom_dir_format', "%ar_album%/%album%")
|
||||||
|
custom_track_format = request.args.get('custom_track_format', "%tracknum%. %music% - %artist%")
|
||||||
|
|
||||||
# Sanitize main and fallback to prevent directory traversal
|
# Sanitize main and fallback to prevent directory traversal
|
||||||
if main:
|
if main:
|
||||||
main = os.path.basename(main)
|
main = os.path.basename(main)
|
||||||
@@ -195,7 +204,20 @@ def handle_artist_download():
|
|||||||
# Create and start the download process.
|
# Create and start the download process.
|
||||||
process = Process(
|
process = Process(
|
||||||
target=download_artist_task,
|
target=download_artist_task,
|
||||||
args=(service, artist_url, main, fallback, quality, fall_quality, real_time, album_type, prg_path, orig_request)
|
args=(
|
||||||
|
service,
|
||||||
|
artist_url,
|
||||||
|
main,
|
||||||
|
fallback,
|
||||||
|
quality,
|
||||||
|
fall_quality,
|
||||||
|
real_time,
|
||||||
|
album_type,
|
||||||
|
prg_path,
|
||||||
|
orig_request,
|
||||||
|
custom_dir_format,
|
||||||
|
custom_track_format
|
||||||
|
)
|
||||||
)
|
)
|
||||||
process.start()
|
process.start()
|
||||||
download_processes[filename] = process
|
download_processes[filename] = process
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
from flask import Blueprint, Response, request
|
|
||||||
import json
|
|
||||||
import os
|
import os
|
||||||
|
import json
|
||||||
|
import traceback
|
||||||
|
from deezspot.spotloader import SpoLogin
|
||||||
|
from deezspot.deezloader import DeeLogin
|
||||||
|
from multiprocessing import Process
|
||||||
import random
|
import random
|
||||||
import string
|
import string
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
|
||||||
from multiprocessing import Process
|
|
||||||
|
|
||||||
playlist_bp = Blueprint('playlist', __name__)
|
playlist_bp = Blueprint('playlist', __name__)
|
||||||
|
|
||||||
@@ -40,7 +41,8 @@ class FlushingFileWrapper:
|
|||||||
def flush(self):
|
def flush(self):
|
||||||
self.file.flush()
|
self.file.flush()
|
||||||
|
|
||||||
def download_task(service, url, main, fallback, quality, fall_quality, real_time, prg_path, orig_request):
|
def download_task(service, url, main, fallback, quality, fall_quality, real_time,
|
||||||
|
prg_path, orig_request, custom_dir_format, custom_track_format):
|
||||||
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:
|
||||||
@@ -65,7 +67,9 @@ def download_task(service, url, main, fallback, quality, fall_quality, real_time
|
|||||||
fallback=fallback,
|
fallback=fallback,
|
||||||
quality=quality,
|
quality=quality,
|
||||||
fall_quality=fall_quality,
|
fall_quality=fall_quality,
|
||||||
real_time=real_time
|
real_time=real_time,
|
||||||
|
custom_dir_format=custom_dir_format,
|
||||||
|
custom_track_format=custom_track_format
|
||||||
)
|
)
|
||||||
flushing_file.write(json.dumps({"status": "complete"}) + "\n")
|
flushing_file.write(json.dumps({"status": "complete"}) + "\n")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -100,6 +104,10 @@ def handle_download():
|
|||||||
real_time_str = request.args.get('real_time', 'false').lower()
|
real_time_str = request.args.get('real_time', 'false').lower()
|
||||||
real_time = real_time_str in ['true', '1', 'yes']
|
real_time = real_time_str in ['true', '1', 'yes']
|
||||||
|
|
||||||
|
# New custom formatting parameters, with defaults.
|
||||||
|
custom_dir_format = request.args.get('custom_dir_format', "%ar_album%/%album%/%copyright%")
|
||||||
|
custom_track_format = request.args.get('custom_track_format', "%tracknum%. %music% - %artist%")
|
||||||
|
|
||||||
if not all([service, url, main]):
|
if not all([service, url, main]):
|
||||||
return Response(
|
return Response(
|
||||||
json.dumps({"error": "Missing parameters"}),
|
json.dumps({"error": "Missing parameters"}),
|
||||||
@@ -117,7 +125,10 @@ def handle_download():
|
|||||||
|
|
||||||
process = Process(
|
process = Process(
|
||||||
target=download_task,
|
target=download_task,
|
||||||
args=(service, url, main, fallback, quality, fall_quality, real_time, prg_path, orig_request)
|
args=(
|
||||||
|
service, url, main, fallback, quality, fall_quality, real_time,
|
||||||
|
prg_path, orig_request, custom_dir_format, custom_track_format
|
||||||
|
)
|
||||||
)
|
)
|
||||||
process.start()
|
process.start()
|
||||||
# Track the running process using the generated filename.
|
# Track the running process using the generated filename.
|
||||||
|
|||||||
@@ -4,7 +4,17 @@ 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, quality=None, fall_quality=None, real_time=False):
|
def download_album(
|
||||||
|
service,
|
||||||
|
url,
|
||||||
|
main,
|
||||||
|
fallback=None,
|
||||||
|
quality=None,
|
||||||
|
fall_quality=None,
|
||||||
|
real_time=False,
|
||||||
|
custom_dir_format="%ar_album%/%album%/%copyright%",
|
||||||
|
custom_track_format="%tracknum%. %music% - %artist%"
|
||||||
|
):
|
||||||
try:
|
try:
|
||||||
if service == 'spotify':
|
if service == 'spotify':
|
||||||
if fallback:
|
if fallback:
|
||||||
@@ -23,7 +33,7 @@ def download_album(service, url, main, fallback=None, quality=None, fall_quality
|
|||||||
dl = DeeLogin(
|
dl = DeeLogin(
|
||||||
arl=deezer_creds.get('arl', ''),
|
arl=deezer_creds.get('arl', ''),
|
||||||
)
|
)
|
||||||
# Download using download_albumspo; pass real_time_dl accordingly
|
# Download using download_albumspo; pass real_time_dl accordingly and the custom formatting
|
||||||
dl.download_albumspo(
|
dl.download_albumspo(
|
||||||
link_album=url,
|
link_album=url,
|
||||||
output_dir="./downloads",
|
output_dir="./downloads",
|
||||||
@@ -32,7 +42,9 @@ def download_album(service, url, main, fallback=None, quality=None, fall_quality
|
|||||||
recursive_download=False,
|
recursive_download=False,
|
||||||
not_interface=False,
|
not_interface=False,
|
||||||
make_zip=False,
|
make_zip=False,
|
||||||
method_save=1
|
method_save=1,
|
||||||
|
custom_dir_format=custom_dir_format,
|
||||||
|
custom_track_format=custom_track_format
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# Load fallback Spotify credentials and attempt download
|
# Load fallback Spotify credentials and attempt download
|
||||||
@@ -49,7 +61,9 @@ def download_album(service, url, main, fallback=None, quality=None, fall_quality
|
|||||||
not_interface=False,
|
not_interface=False,
|
||||||
method_save=1,
|
method_save=1,
|
||||||
make_zip=False,
|
make_zip=False,
|
||||||
real_time_dl=real_time
|
real_time_dl=real_time,
|
||||||
|
custom_dir_format=custom_dir_format,
|
||||||
|
custom_track_format=custom_track_format
|
||||||
)
|
)
|
||||||
except Exception as e2:
|
except Exception as e2:
|
||||||
# If fallback also fails, raise an error indicating both attempts failed
|
# If fallback also fails, raise an error indicating both attempts failed
|
||||||
@@ -73,7 +87,9 @@ def download_album(service, url, main, fallback=None, quality=None, fall_quality
|
|||||||
not_interface=False,
|
not_interface=False,
|
||||||
method_save=1,
|
method_save=1,
|
||||||
make_zip=False,
|
make_zip=False,
|
||||||
real_time_dl=real_time
|
real_time_dl=real_time,
|
||||||
|
custom_dir_format=custom_dir_format,
|
||||||
|
custom_track_format=custom_track_format
|
||||||
)
|
)
|
||||||
elif service == 'deezer':
|
elif service == 'deezer':
|
||||||
if quality is None:
|
if quality is None:
|
||||||
@@ -93,7 +109,9 @@ def download_album(service, url, main, fallback=None, quality=None, fall_quality
|
|||||||
recursive_quality=True,
|
recursive_quality=True,
|
||||||
recursive_download=False,
|
recursive_download=False,
|
||||||
method_save=1,
|
method_save=1,
|
||||||
make_zip=False
|
make_zip=False,
|
||||||
|
custom_dir_format=custom_dir_format,
|
||||||
|
custom_track_format=custom_track_format
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Unsupported service: {service}")
|
raise ValueError(f"Unsupported service: {service}")
|
||||||
|
|||||||
@@ -43,15 +43,17 @@ def get_artist_discography(url, album_type='album,single,compilation,appears_on'
|
|||||||
|
|
||||||
|
|
||||||
def download_artist_albums(service, artist_url, main, fallback=None, quality=None,
|
def download_artist_albums(service, artist_url, main, fallback=None, quality=None,
|
||||||
fall_quality=None, real_time=False, album_type='album,single,compilation,appears_on'):
|
fall_quality=None, real_time=False, album_type='album,single,compilation,appears_on',
|
||||||
|
custom_dir_format="%ar_album%/%album%/%copyright%",
|
||||||
|
custom_track_format="%tracknum%. %music% - %artist%"):
|
||||||
try:
|
try:
|
||||||
discography = get_artist_discography(artist_url, album_type=album_type)
|
discography = get_artist_discography(artist_url, album_type=album_type)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log_json({"status": "error", "message": f"Error retrieving artist discography: {e}"})
|
log_json({"status": "error", "message": f"Error retrieving artist discography: {e}"})
|
||||||
raise
|
raise
|
||||||
albums = discography.get('items', [])
|
albums = discography.get('items', [])
|
||||||
# Extract artist name from the first album's artists
|
# Extract artist name from the first album's artists as fallback.
|
||||||
artist_name = artist_url # default fallback
|
artist_name = artist_url
|
||||||
if albums:
|
if albums:
|
||||||
first_album = albums[0]
|
first_album = albums[0]
|
||||||
artists = first_album.get('artists', [])
|
artists = first_album.get('artists', [])
|
||||||
@@ -68,7 +70,13 @@ def download_artist_albums(service, artist_url, main, fallback=None, quality=Non
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
|
|
||||||
log_json({"status": "initializing", "type": "artist", "artist": artist_name, "total_albums": len(albums), "album_type": album_type})
|
log_json({
|
||||||
|
"status": "initializing",
|
||||||
|
"type": "artist",
|
||||||
|
"artist": artist_name,
|
||||||
|
"total_albums": len(albums),
|
||||||
|
"album_type": album_type
|
||||||
|
})
|
||||||
|
|
||||||
for album in albums:
|
for album in albums:
|
||||||
try:
|
try:
|
||||||
@@ -95,7 +103,9 @@ def download_artist_albums(service, artist_url, main, fallback=None, quality=Non
|
|||||||
fallback=fallback,
|
fallback=fallback,
|
||||||
quality=quality,
|
quality=quality,
|
||||||
fall_quality=fall_quality,
|
fall_quality=fall_quality,
|
||||||
real_time=real_time
|
real_time=real_time,
|
||||||
|
custom_dir_format=custom_dir_format,
|
||||||
|
custom_track_format=custom_track_format
|
||||||
)
|
)
|
||||||
|
|
||||||
except Exception as album_error:
|
except Exception as album_error:
|
||||||
@@ -113,4 +123,4 @@ def download_artist_albums(service, artist_url, main, fallback=None, quality=Non
|
|||||||
"type": "artist",
|
"type": "artist",
|
||||||
"artist": artist_name,
|
"artist": artist_name,
|
||||||
"album_type": album_type
|
"album_type": album_type
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -4,7 +4,17 @@ 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, quality=None, fall_quality=None, real_time=False):
|
def download_playlist(
|
||||||
|
service,
|
||||||
|
url,
|
||||||
|
main,
|
||||||
|
fallback=None,
|
||||||
|
quality=None,
|
||||||
|
fall_quality=None,
|
||||||
|
real_time=False,
|
||||||
|
custom_dir_format="%ar_album%/%album%/%copyright%",
|
||||||
|
custom_track_format="%tracknum%. %music% - %artist%"
|
||||||
|
):
|
||||||
try:
|
try:
|
||||||
if service == 'spotify':
|
if service == 'spotify':
|
||||||
if fallback:
|
if fallback:
|
||||||
@@ -23,7 +33,7 @@ def download_playlist(service, url, main, fallback=None, quality=None, fall_qual
|
|||||||
dl = DeeLogin(
|
dl = DeeLogin(
|
||||||
arl=deezer_creds.get('arl', ''),
|
arl=deezer_creds.get('arl', ''),
|
||||||
)
|
)
|
||||||
# Download using download_playlistspo
|
# Download using download_playlistspo; pass the custom formatting parameters.
|
||||||
dl.download_playlistspo(
|
dl.download_playlistspo(
|
||||||
link_playlist=url,
|
link_playlist=url,
|
||||||
output_dir="./downloads",
|
output_dir="./downloads",
|
||||||
@@ -32,7 +42,9 @@ def download_playlist(service, url, main, fallback=None, quality=None, fall_qual
|
|||||||
recursive_download=False,
|
recursive_download=False,
|
||||||
not_interface=False,
|
not_interface=False,
|
||||||
make_zip=False,
|
make_zip=False,
|
||||||
method_save=1
|
method_save=1,
|
||||||
|
custom_dir_format=custom_dir_format,
|
||||||
|
custom_track_format=custom_track_format
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# Load fallback Spotify credentials and attempt download
|
# Load fallback Spotify credentials and attempt download
|
||||||
@@ -49,7 +61,9 @@ def download_playlist(service, url, main, fallback=None, quality=None, fall_qual
|
|||||||
not_interface=False,
|
not_interface=False,
|
||||||
method_save=1,
|
method_save=1,
|
||||||
make_zip=False,
|
make_zip=False,
|
||||||
real_time_dl=real_time
|
real_time_dl=real_time,
|
||||||
|
custom_dir_format=custom_dir_format,
|
||||||
|
custom_track_format=custom_track_format
|
||||||
)
|
)
|
||||||
except Exception as e2:
|
except Exception as e2:
|
||||||
# If fallback also fails, raise an error indicating both attempts failed
|
# If fallback also fails, raise an error indicating both attempts failed
|
||||||
@@ -73,12 +87,14 @@ def download_playlist(service, url, main, fallback=None, quality=None, fall_qual
|
|||||||
not_interface=False,
|
not_interface=False,
|
||||||
method_save=1,
|
method_save=1,
|
||||||
make_zip=False,
|
make_zip=False,
|
||||||
real_time_dl=real_time
|
real_time_dl=real_time,
|
||||||
|
custom_dir_format=custom_dir_format,
|
||||||
|
custom_track_format=custom_track_format
|
||||||
)
|
)
|
||||||
elif service == 'deezer':
|
elif service == 'deezer':
|
||||||
if quality is None:
|
if quality is None:
|
||||||
quality = 'FLAC'
|
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'))
|
||||||
with open(creds_path, 'r') as f:
|
with open(creds_path, 'r') as f:
|
||||||
@@ -93,7 +109,9 @@ def download_playlist(service, url, main, fallback=None, quality=None, fall_qual
|
|||||||
recursive_quality=False,
|
recursive_quality=False,
|
||||||
recursive_download=False,
|
recursive_download=False,
|
||||||
method_save=1,
|
method_save=1,
|
||||||
make_zip=False
|
make_zip=False,
|
||||||
|
custom_dir_format=custom_dir_format,
|
||||||
|
custom_track_format=custom_track_format
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Unsupported service: {service}")
|
raise ValueError(f"Unsupported service: {service}")
|
||||||
|
|||||||
Reference in New Issue
Block a user