Skip to content
Snippets Groups Projects
Commit 1d33802a authored by EorlBruder's avatar EorlBruder Committed by Georg Krause
Browse files

Adds generate-playlist command

parent 0c6af1dc
No related branches found
No related tags found
1 merge request!13Added generate-playlist command to tracks
dist dist
build build
funkwhale funkwhale
venv
.idea
funkwhale_cli.egg-info
\ No newline at end of file
...@@ -56,6 +56,9 @@ funkwhale tracks ls --filter "favorites=true" --ids --limit 0 | xargs funkwhale ...@@ -56,6 +56,9 @@ funkwhale tracks ls --filter "favorites=true" --ids --limit 0 | xargs funkwhale
# Download a track and pipe the output directly to VLC # Download a track and pipe the output directly to VLC
funkwhale tracks download <track_id> | cvlc - funkwhale tracks download <track_id> | cvlc -
# Generate a playlist-file from a funkwhale playlist in a custom directory
funkwhale playlist tracks --ids <playlist_id> | funkwhale tracks generate-playlist -d ~/Music -t "{artist}/{album} ({year})/{title}.{extension}"
# Delete your library # Delete your library
funkwhale libraries rm <library_id> funkwhale libraries rm <library_id>
......
...@@ -9,6 +9,10 @@ from . import base ...@@ -9,6 +9,10 @@ from . import base
from .. import logs from .. import logs
from .. import utils from .. import utils
TEMPLATE_ENV_VAR = "FUNKWHALE_DOWNLOAD_PATH_TEMPLATE"
TEMPLATE_DEFAULT = "{artist} - {album} - {title}.{extension}"
@base.cli.group() @base.cli.group()
@click.pass_context @click.pass_context
...@@ -52,6 +56,21 @@ async def get_track_download_url(id, remote, format=None): ...@@ -52,6 +56,21 @@ async def get_track_download_url(id, remote, format=None):
return download_url, format, payload return download_url, format, payload
def extract_filename_params(track_data, format):
filename_params = utils.flatten(track_data)
filename_params["album"] = filename_params["album_title"]
filename_params["artist"] = filename_params["artist_name"]
filename_params["extension"] = format
filename_params["year"] = (
filename_params["album_release_date"][:4]
if filename_params["album_release_date"]
else None
)
return {
k: utils.sanitize_recursive(v) for k, v in filename_params.items()
}
@tracks.command("download") @tracks.command("download")
@click.argument("id", nargs=-1, required=True) @click.argument("id", nargs=-1, required=True)
@click.option("--format", "-f") @click.option("--format", "-f")
...@@ -62,19 +81,19 @@ async def get_track_download_url(id, remote, format=None): ...@@ -62,19 +81,19 @@ async def get_track_download_url(id, remote, format=None):
@click.option( @click.option(
"-t", "-t",
"--template", "--template",
default="{artist} - {album} - {title}.{extension}", default=TEMPLATE_DEFAULT,
envvar="FUNKWHALE_DOWNLOAD_PATH_TEMPLATE", envvar=TEMPLATE_ENV_VAR,
) )
@click.pass_context @click.pass_context
@base.async_command @base.async_command
async def track_download( async def track_download(
ctx, id, format, directory, template, overwrite, ignore_errors, skip_existing ctx, id, format, directory, template, overwrite, ignore_errors, skip_existing
): ):
async with ctx.obj["remote"]: async with ctx.obj["remote"]:
progressbar = tqdm.tqdm(id, unit="Files") progressbar = tqdm.tqdm(id, unit="Files")
for i in progressbar: for i in progressbar:
try: try:
download_url, format, track_data = await get_track_download_url( download_url, extension, track_data = await get_track_download_url(
i, ctx.obj["remote"], format=format i, ctx.obj["remote"], format=format
) )
except click.ClickException as e: except click.ClickException as e:
...@@ -82,18 +101,8 @@ async def track_download( ...@@ -82,18 +101,8 @@ async def track_download(
continue continue
logs.logger.info("Downloading from {}".format(download_url)) logs.logger.info("Downloading from {}".format(download_url))
filename_params = utils.flatten(track_data) filename_params = extract_filename_params(track_data, extension)
filename_params["album"] = filename_params["album_title"]
filename_params["artist"] = filename_params["artist_name"]
filename_params["extension"] = format
filename_params["year"] = (
filename_params["album_release_date"][:4]
if filename_params["album_release_date"]
else None
)
filename_params = {
k: utils.sanitize_recursive(v) for k, v in filename_params.items()
}
if directory: if directory:
filename = template.format(**filename_params) filename = template.format(**filename_params)
full_path = os.path.join(directory, filename) full_path = os.path.join(directory, filename)
...@@ -113,7 +122,7 @@ async def track_download( ...@@ -113,7 +122,7 @@ async def track_download(
) )
async with ctx.obj["remote"].request( async with ctx.obj["remote"].request(
"get", download_url, timeout=0 "get", download_url, timeout=0
) as response: ) as response:
try: try:
response.raise_for_status() response.raise_for_status()
...@@ -144,3 +153,55 @@ async def track_download( ...@@ -144,3 +153,55 @@ async def track_download(
break break
out.write(chunk) out.write(chunk)
logs.logger.info("Download complete") logs.logger.info("Download complete")
async def get_track_data(id, remote, format=None):
result = await remote.request("get", "api/v1/tracks/{}/".format(id))
result.raise_for_status()
payload = await result.json()
if not format and payload["uploads"]:
format = payload["uploads"][0]["extension"]
return format, payload
@tracks.command("generate-playlist")
@click.argument("id", nargs=-1, required=True)
@click.option("--format", "-f")
@click.option("-d", "--directory", type=click.Path(exists=True))
@click.option(
"-t",
"--template",
default=TEMPLATE_DEFAULT,
envvar=TEMPLATE_ENV_VAR,
)
@click.option("-b", "--base-path", default="./")
@click.option("-n", "--name", default="playlist")
@click.pass_context
@base.async_command
async def track_generate_playlist(
ctx, id, format, directory, template, base_path, name
):
async with ctx.obj["remote"]:
playlist = []
for i in id:
extension, track_data = await get_track_data(
i, ctx.obj["remote"], format=format
)
filename_params = extract_filename_params(track_data, extension)
filename = base_path + template.format(**filename_params)
playlist.append(filename)
if directory:
filename = name + ".m3u8"
full_path = os.path.join(directory, filename)
logs.logger.info("Writing playlist to {}".format(full_path))
with open(full_path, "w") as out:
for track in playlist:
out.write(track + "\n")
else:
out = click.get_binary_stream("stdout")
for track in playlist:
out.write(bytes(track + "\n", "utf8"))
logs.logger.info("Export of playlist complete")
...@@ -388,6 +388,82 @@ def test_tracks_ls(cli_ctx, session, responses, get_requests): ...@@ -388,6 +388,82 @@ def test_tracks_ls(cli_ctx, session, responses, get_requests):
assert len(requests) == 1 assert len(requests) == 1
def test_tracks_generate_playlist(cli_ctx, session, responses, get_requests, capfd):
expected = "./Test-Artist - Test-Album - Test-Track.flac\n"
command = cli.tracks.track_generate_playlist
url = "https://test.funkwhale/api/v1/tracks/1/"
responses.get(
url,
payload={"uploads": [{"extension": "flac"}], "album": {"title": "Test-Album", "release_date": "2021-02-01"},
"artist": {"name": "Test-Artist"}, "title": "Test-Track"}
)
command.callback(
id="1",
format=None,
directory=None,
template="{artist} - {album} - {title}.{extension}",
base_path="./",
name="playlist",
)
requests = get_requests("get", url)
assert len(requests) == 1
out, err = capfd.readouterr()
assert out == expected
def test_tracks_generate_playlist_base_path(cli_ctx, session, responses, get_requests, capfd):
expected = "/different_base/Test-Artist - Test-Album - Test-Track.flac\n"
command = cli.tracks.track_generate_playlist
url = "https://test.funkwhale/api/v1/tracks/1/"
responses.get(
url,
payload={"uploads": [{"extension": "flac"}], "album": {"title": "Test-Album", "release_date": "2021-02-01"},
"artist": {"name": "Test-Artist"}, "title": "Test-Track"}
)
command.callback(
id="1",
format=None,
directory=None,
template="{artist} - {album} - {title}.{extension}",
base_path="/different_base/",
name="playlist",
)
requests = get_requests("get", url)
assert len(requests) == 1
out, err = capfd.readouterr()
assert out == expected
def test_tracks_generate_playlist_format(cli_ctx, session, responses, get_requests, capfd):
expected = "./Test-Artist - Test-Album - Test-Track.mp3\n"
command = cli.tracks.track_generate_playlist
url = "https://test.funkwhale/api/v1/tracks/1/"
responses.get(
url,
payload={"uploads": [{"extension": "flac"}], "album": {"title": "Test-Album", "release_date": "2021-02-01"},
"artist": {"name": "Test-Artist"}, "title": "Test-Track"}
)
command.callback(
id="1",
format="mp3",
directory=None,
template="{artist} - {album} - {title}.{extension}",
base_path="./",
name="playlist",
)
requests = get_requests("get", url)
assert len(requests) == 1
out, err = capfd.readouterr()
assert out == expected
def test_artists_ls(cli_ctx, session, responses, get_requests): def test_artists_ls(cli_ctx, session, responses, get_requests):
command = cli.artists.artists_ls command = cli.artists.artists_ls
url = "https://test.funkwhale/api/v1/artists/?ordering=-creation_date&page=1&page_size=5&q=hello" url = "https://test.funkwhale/api/v1/artists/?ordering=-creation_date&page=1&page_size=5&q=hello"
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment