Adding a command line client
This commit is contained in:
127
client/client.py
Normal file
127
client/client.py
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
'''
|
||||||
|
Simple command line client to remove vocals from audio/video tracks.
|
||||||
|
'''
|
||||||
|
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
|
||||||
|
import click
|
||||||
|
import requests
|
||||||
|
from pygments import highlight, lexers, formatters
|
||||||
|
|
||||||
|
|
||||||
|
API_ENDPOINT_URL = os.getenv('API_ENDPOINT_URL')
|
||||||
|
|
||||||
|
|
||||||
|
def api_req(ctx: click.core.Context, method: str, url: str, **kwargs):
|
||||||
|
method = method.lower()
|
||||||
|
if method not in ['get', 'post']:
|
||||||
|
raise ValueError(f'Unsupported HTTP method "{method}"')
|
||||||
|
|
||||||
|
actual_url = ctx.obj['url'](url)
|
||||||
|
|
||||||
|
if ctx.obj['verbose']:
|
||||||
|
print(f'GET {actual_url}')
|
||||||
|
|
||||||
|
response = getattr(requests, method)(actual_url, **kwargs)
|
||||||
|
|
||||||
|
if ctx.obj['verbose']:
|
||||||
|
print(f'Server response status: {response.status_code}')
|
||||||
|
echo_obj(response.json())
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
def api_get(ctx: click.core.Context, url: str, **kwargs):
|
||||||
|
return api_req(ctx, 'GET', url, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def api_post(ctx: click.core.Context, url: str, **kwargs):
|
||||||
|
return api_req(ctx, 'POST', url, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def highlight_json(obj):
|
||||||
|
formatted_json = json.dumps(obj, sort_keys=True, indent=4)
|
||||||
|
return highlight(formatted_json, lexers.JsonLexer(), formatters.TerminalFormatter())
|
||||||
|
|
||||||
|
|
||||||
|
def echo_obj(obj):
|
||||||
|
click.echo(highlight_json(obj))
|
||||||
|
|
||||||
|
|
||||||
|
@click.group()
|
||||||
|
@click.option('--verbose', '-v', is_flag=True, help='Enables verbose output.')
|
||||||
|
@click.option('--endpoint', '-e', default=API_ENDPOINT_URL, show_default=True)
|
||||||
|
@click.pass_context
|
||||||
|
def cli(ctx: click.core.Context, verbose: bool, endpoint: str):
|
||||||
|
ctx.obj = {
|
||||||
|
'verbose': verbose,
|
||||||
|
'endpoint': endpoint,
|
||||||
|
'url': lambda s: f'{endpoint}{s}'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command('version')
|
||||||
|
@click.pass_context
|
||||||
|
def get_version(ctx: click.core.Context):
|
||||||
|
'''Gets the service version'''
|
||||||
|
response = api_get(ctx, '/')
|
||||||
|
echo_obj(response.json())
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command('list')
|
||||||
|
@click.pass_context
|
||||||
|
def list_jobs(ctx: click.core.Context):
|
||||||
|
'''List conversion jobs'''
|
||||||
|
response = api_get(ctx, '/jobs')
|
||||||
|
echo_obj(response.json())
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command('remove-vocals')
|
||||||
|
@click.option('--file', type=click.File(mode='rb'), required=True)
|
||||||
|
@click.option('--output-path', '-o', type=click.Path(writable=True))
|
||||||
|
@click.pass_context
|
||||||
|
def remove_vocals(ctx: click.core.Context, file: click.File, output_path: click.Path):
|
||||||
|
'''Removes the vocal part from an audio/video file'''
|
||||||
|
response = api_post(ctx, '/jobs')
|
||||||
|
job = response.json()
|
||||||
|
job_id = job['job_id']
|
||||||
|
upload_data = job['upload_data']
|
||||||
|
files = {'file': file}
|
||||||
|
|
||||||
|
upload_started = time.time()
|
||||||
|
response = requests.post(upload_data['url'],
|
||||||
|
data=upload_data['fields'],
|
||||||
|
files=files)
|
||||||
|
|
||||||
|
upload_time = time.time() - upload_started
|
||||||
|
print(f"File uploaded in {upload_time}s")
|
||||||
|
|
||||||
|
response = api_post(ctx, f'/jobs/{job_id}/process')
|
||||||
|
print(f'Processing file with job id {job_id}')
|
||||||
|
|
||||||
|
processing_started = time.time()
|
||||||
|
|
||||||
|
status_changed = False
|
||||||
|
job = None
|
||||||
|
while not status_changed:
|
||||||
|
response = api_get(ctx, f'/jobs/{job_id}')
|
||||||
|
|
||||||
|
job = response.json()
|
||||||
|
status_changed = job['status'] != 'processing'
|
||||||
|
time.sleep(5)
|
||||||
|
|
||||||
|
processing_time = time.time() - processing_started
|
||||||
|
print(f"File processed in {processing_time}s")
|
||||||
|
|
||||||
|
response = requests.get(job['output_url'], stream=True)
|
||||||
|
with open(output_path, 'wb') as f:
|
||||||
|
for chunk in response.raw:
|
||||||
|
f.write(chunk)
|
||||||
|
|
||||||
|
print(f"Saved file to: {output_path}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
cli()
|
||||||
3
client/requirements.txt
Normal file
3
client/requirements.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
click
|
||||||
|
pygments
|
||||||
|
requests
|
||||||
@@ -21,6 +21,7 @@ OUTPUT_FILENAME_FORMAT = os.getenv('OUTPUT_FILENAME_FORMAT', '{instrument}.{code
|
|||||||
QUEUE_NAME = os.getenv('QUEUE_NAME')
|
QUEUE_NAME = os.getenv('QUEUE_NAME')
|
||||||
POLLING_INTERVAL = int(os.getenv('POLLING_INTERVAL', 5))
|
POLLING_INTERVAL = int(os.getenv('POLLING_INTERVAL', 5))
|
||||||
OUTPUT_BUCKET_NAME = os.getenv('OUTPUT_BUCKET_NAME')
|
OUTPUT_BUCKET_NAME = os.getenv('OUTPUT_BUCKET_NAME')
|
||||||
|
OUTPUT_BUCKET_REGION= os.getenv('OUTPUT_BUCKET_REGION', 'eu-west-1')
|
||||||
TRACKS_TABLE_NAME = os.getenv('TRACKS_TABLE_NAME')
|
TRACKS_TABLE_NAME = os.getenv('TRACKS_TABLE_NAME')
|
||||||
USE_MULTICHANNEL_WIENER_FILTERING = False
|
USE_MULTICHANNEL_WIENER_FILTERING = False
|
||||||
|
|
||||||
@@ -79,6 +80,7 @@ def poll_for_sqs_message(queue_name: str):
|
|||||||
body = json.loads(message['Body'])
|
body = json.loads(message['Body'])
|
||||||
job_id = body['job_id']
|
job_id = body['job_id']
|
||||||
output_s3_url = f"s3://{OUTPUT_BUCKET_NAME}/track_{job_id}.mp3"
|
output_s3_url = f"s3://{OUTPUT_BUCKET_NAME}/track_{job_id}.mp3"
|
||||||
|
download_url = f'https://{OUTPUT_BUCKET_NAME}.s3-{OUTPUT_BUCKET_REGION}.amazonaws.com/track_{job_id}.mp3'
|
||||||
logging.info(f'Start separating {job_id} -> {output_s3_url}')
|
logging.info(f'Start separating {job_id} -> {output_s3_url}')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -94,7 +96,7 @@ def poll_for_sqs_message(queue_name: str):
|
|||||||
Key={'id': {'S': job_id}},
|
Key={'id': {'S': job_id}},
|
||||||
AttributeUpdates={
|
AttributeUpdates={
|
||||||
'status': {'Value': {'S': 'successful'}},
|
'status': {'Value': {'S': 'successful'}},
|
||||||
'output_url': {'Value': {'S': output_s3_url}}
|
'output_url': {'Value': {'S': download_url}}
|
||||||
})
|
})
|
||||||
except:
|
except:
|
||||||
logging.exception('Processing failed for some reason')
|
logging.exception('Processing failed for some reason')
|
||||||
|
|||||||
Reference in New Issue
Block a user