added endpoints for custom dir and track format

This commit is contained in:
cool.gitter.choco
2025-02-06 14:14:54 -06:00
parent 82bea2da12
commit 2a822c5a9f
6 changed files with 129 additions and 31 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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}")

View File

@@ -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:

View File

@@ -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}")