Skip to content
Snippets Groups Projects
Verified Commit d6c60983 authored by Eliot Berriot's avatar Eliot Berriot
Browse files

Added playlist track listing

parent b947f834
Branches
No related tags found
No related merge requests found
Pipeline #4565 passed
...@@ -92,6 +92,10 @@ def ignore_aiohttp_ssl_eror(loop): ...@@ -92,6 +92,10 @@ def ignore_aiohttp_ssl_eror(loop):
loop.set_exception_handler(ignore_ssl_error) loop.set_exception_handler(ignore_ssl_error)
def noop_decorator(f):
return f
def URL(v): def URL(v):
if v is NOOP: if v is NOOP:
raise click.ClickException( raise click.ClickException(
...@@ -385,25 +389,57 @@ def get_pagination_data(payload): ...@@ -385,25 +389,57 @@ def get_pagination_data(payload):
return data return data
def get_ls_command(group, endpoint, output_conf): def get_ls_command(
group,
endpoint,
output_conf,
pagination=True,
filter=True,
ordering=True,
with_id=False,
name="ls",
):
available_fields = sorted( available_fields = sorted(
set(output_conf["labels"]) | set(output.FIELDS["*"].keys()) set(output_conf["labels"]) | set(output.FIELDS["*"].keys())
) )
id_decorator = click.argument("id") if with_id else noop_decorator
page_decorator = (
click.option("--page", "-p", type=click.INT, default=1)
if pagination
else noop_decorator
)
page_size_decorator = (
click.option("--page-size", "-s", type=click.INT, default=None)
if pagination
else noop_decorator
)
limit_decorator = (
click.option("--limit", "-l", type=click.INT, default=1)
if pagination
else noop_decorator
)
ordering_decorator = (
click.option("--ordering", "-o", default=None) if ordering else noop_decorator
)
filter_decorator = (
click.option("--filter", "-f", multiple=True) if filter else noop_decorator
)
@group.command("ls") @group.command(name)
@id_decorator
@click.argument("query", nargs=-1) @click.argument("query", nargs=-1)
@RAW_DECORATOR @RAW_DECORATOR
@click.option( @click.option(
"--format", "-t", type=click.Choice(output.TABLE_FORMATS), default="simple" "--format", "-t", type=click.Choice(output.TABLE_FORMATS), default="simple"
) )
@click.option("--no-headers", "-h", is_flag=True, default=False) @click.option("--no-headers", "-h", is_flag=True, default=False)
@click.option("--page", "-p", type=click.INT, default=1)
@click.option("--page-size", "-s", type=click.INT, default=None)
@click.option("--ordering", "-o", default=None)
@click.option("--filter", "-f", multiple=True)
@click.option("--ids", "-i", is_flag=True) @click.option("--ids", "-i", is_flag=True)
@click.option("--limit", "-l", type=click.INT, default=1) @page_decorator
@page_size_decorator
@ordering_decorator
@filter_decorator
@limit_decorator
@click.option( @click.option(
"--column", "--column",
"-c", "-c",
...@@ -414,33 +450,32 @@ def get_ls_command(group, endpoint, output_conf): ...@@ -414,33 +450,32 @@ def get_ls_command(group, endpoint, output_conf):
) )
@click.pass_context @click.pass_context
@async_command @async_command
async def ls( async def ls(ctx, raw, column, format, no_headers, ids, **kwargs):
ctx, id = kwargs.get("id")
raw, limit = kwargs.get("limit")
page, page = kwargs.get("page")
page_size, page_size = kwargs.get("page_size")
ordering, ordering = kwargs.get("ordering")
filter, filter = kwargs.get("filter")
query, query = kwargs.get("query")
column,
format,
no_headers,
ids,
limit,
):
if ids: if ids:
no_headers = True no_headers = True
column = [output_conf.get("id_field", "UUID")] column = [output_conf.get("id_field", "UUID")]
format = "plain" format = "plain"
base_url = endpoint
if with_id:
base_url = base_url.format(id)
next_page_url = None next_page_url = None
page_count = 0 page_count = 0
while True: while True:
if limit and page_count >= limit: if limit and page_count >= limit:
break break
async with ctx.obj["remote"]: async with ctx.obj["remote"]:
if page_count == 0: if not pagination or page_count == 0:
url = endpoint url = base_url
params = {"page": page} params = {}
if page:
params["page"] = page
if page_size: if page_size:
params["page_size"] = page_size params["page_size"] = page_size
if ordering: if ordering:
...@@ -460,7 +495,7 @@ def get_ls_command(group, endpoint, output_conf): ...@@ -460,7 +495,7 @@ def get_ls_command(group, endpoint, output_conf):
result = await ctx.obj["remote"].request("get", url, params=params) result = await ctx.obj["remote"].request("get", url, params=params)
result.raise_for_status() result.raise_for_status()
payload = await result.json() payload = await result.json()
next_page_url = payload["next"] next_page_url = payload.get("next")
page_count += 1 page_count += 1
if raw: if raw:
click.echo(json.dumps(payload, sort_keys=True, indent=4)) click.echo(json.dumps(payload, sort_keys=True, indent=4))
...@@ -474,6 +509,8 @@ def get_ls_command(group, endpoint, output_conf): ...@@ -474,6 +509,8 @@ def get_ls_command(group, endpoint, output_conf):
headers=not no_headers, headers=not no_headers,
) )
) )
if not pagination:
break
pagination_data = get_pagination_data(payload) pagination_data = get_pagination_data(payload)
if pagination_data["page_size"]: if pagination_data["page_size"]:
start = ( start = (
...@@ -499,17 +536,20 @@ def get_ls_command(group, endpoint, output_conf): ...@@ -499,17 +536,20 @@ def get_ls_command(group, endpoint, output_conf):
return ls return ls
def get_show_command(group, url_template, output_conf, name='show', force_id=None): def get_show_command(group, url_template, output_conf, name="show", force_id=None):
available_fields = sorted( available_fields = sorted(
set(output_conf["labels"]) | set(output.FIELDS["*"].keys()) set(output_conf["labels"]) | set(output.FIELDS["*"].keys())
) )
if force_id: if force_id:
def id_decorator(f): def id_decorator(f):
@functools.wraps(f) @functools.wraps(f)
def inner(raw, column, format): def inner(raw, column, format):
return f(raw, force_id, column, format) return f(raw, force_id, column, format)
return inner return inner
else: else:
id_decorator = click.argument("id") id_decorator = click.argument("id")
...@@ -529,16 +569,12 @@ def get_show_command(group, url_template, output_conf, name='show', force_id=Non ...@@ -529,16 +569,12 @@ def get_show_command(group, url_template, output_conf, name='show', force_id=Non
) )
@click.pass_context @click.pass_context
@async_command @async_command
async def show( async def show(ctx, raw, id, column, format):
ctx,
raw,
id,
column,
format,
):
async with ctx.obj["remote"]: async with ctx.obj["remote"]:
async with ctx.obj["remote"].request("get", url_template.format(id)) as result: async with ctx.obj["remote"].request(
"get", url_template.format(id)
) as result:
result.raise_for_status() result.raise_for_status()
payload = await result.json() payload = await result.json()
if raw: if raw:
...@@ -1052,6 +1088,42 @@ async def playlists_create(ctx, raw, name, visibility): ...@@ -1052,6 +1088,42 @@ async def playlists_create(ctx, raw, name, visibility):
) )
@playlists.command("tracks-add")
@click.argument("id")
@click.argument("track", nargs=-1)
@click.option(
"--no-duplicates",
is_flag=True,
default=False,
help="Prevent insertion of tracks that already exist in the playlist",
)
@click.pass_context
@async_command
async def playlists_tracks_add(ctx, id, track, no_duplicates):
async with ctx.obj["remote"]:
async with ctx.obj["remote"].request(
"post",
"api/v1/playlists/{}/".format(id),
data={"tracks": track, "allow_duplicates": not no_duplicates},
) as response:
response.raise_for_status()
playlists_tracks = get_ls_command(
playlists,
"api/v1/playlists/{}/tracks/",
name="tracks",
with_id=True,
pagination=False,
ordering=False,
filter=False,
output_conf={
"labels": ["Position", "ID", "Title", "Artist", "Album", "Created"],
"id_field": "ID",
"type": "PLAYLIST_TRACK",
},
)
@cli.group() @cli.group()
@click.pass_context @click.pass_context
...@@ -1077,8 +1149,8 @@ users_me = get_show_command( ...@@ -1077,8 +1149,8 @@ users_me = get_show_command(
], ],
"type": "USER", "type": "USER",
}, },
force_id='me', force_id="me",
name='me', name="me",
) )
......
...@@ -50,6 +50,12 @@ FIELDS = { ...@@ -50,6 +50,12 @@ FIELDS = {
"Tracks Count": {"field": "tracks_count"}, "Tracks Count": {"field": "tracks_count"},
"User": {"field": "user.username"}, "User": {"field": "user.username"},
}, },
"PLAYLIST_TRACK": {
"Position": {"field": "index"},
"Title": {"field": "track.title"},
"Artist": {"field": "track.artist.name"},
"Album": {"field": "track.album.title"},
},
"USER": { "USER": {
"Username": {"field": "username"}, "Username": {"field": "username"},
"Federation ID": {"field": "full_username"}, "Federation ID": {"field": "full_username"},
......
...@@ -337,3 +337,28 @@ def test_playlists_rm(cli_ctx, session, responses, get_requests): ...@@ -337,3 +337,28 @@ def test_playlists_rm(cli_ctx, session, responses, get_requests):
assert len(get_requests("delete", url + "1/")) == 1 assert len(get_requests("delete", url + "1/")) == 1
assert len(get_requests("delete", url + "42/")) == 1 assert len(get_requests("delete", url + "42/")) == 1
def test_playlists_tracks_add(cli_ctx, session, responses, get_requests):
command = cli.playlists_tracks_add
url = "https://test.funkwhale/api/v1/playlists/66/"
responses.post(url)
command.callback(id=66, track=[1, 42], no_duplicates=True, _async_reraise=True)
requests = get_requests("post", url)
assert len(requests) == 1
assert requests[0].kwargs["data"] == {"tracks": [1, 42], "allow_duplicates": False}
def test_playlists_tracks(cli_ctx, session, responses, get_requests):
command = cli.playlists_tracks
url = "https://test.funkwhale/api/v1/playlists/66/tracks/"
responses.get(url, payload={"results": [], "count": 0})
command.callback(
id=66, raw=False, column=None, format=None, no_headers=False, ids=False
)
requests = get_requests("get", url)
assert len(requests) == 1
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment