AURA Plugin
***********

This plugin is a server implementation of the AURA specification using
the Flask framework. AURA is still a work in progress and doesn't yet
have a stable version, but this server should be kept up to date. You
are advised to read the Issues section.


Install
=======

The "aura" plugin depends on Flask, which can be installed using
"python -m pip install flask". Then you can enable the "aura" plugin
in your configuration (see Using Plugins).

It is likely that you will need to enable Cross-Origin Resource
Sharing (CORS), which introduces an additional dependency: flask-cors.
This can be installed with "python -m pip install flask-cors".

If Pillow is installed ("python -m pip install Pillow") then the
optional "width" and "height" attributes are included in image
resource objects.


Usage
=====

Use "beet aura" to start the AURA server. By default Flask's built-in
server is used, which will give a warning about using it in a
production environment. It is safe to ignore this warning if the
server will have only a few users.

Alternatively, you can use "beet aura -d" to start the server in
development mode, which will reload the server every time the AURA
plugin file is changed.

You can specify the hostname and port number used by the server in
your configuration file. For more detail see the Configuration section
below.

If you would prefer to use a different WSGI server, such as gunicorn
or uWSGI, then see Using an External WSGI Server.

AURA is designed to separate the client and server functionality. This
plugin provides the server but not the client, so unless you like
looking at JSON you will need a separate client. Currently the only
client is AURA Web Client. In order to use a local browser client with
"file:///" see Cross-Origin Resource Sharing (CORS).

By default the API is served under http://127.0.0.1:8337/aura/. For
example information about the track with an id of 3 can be obtained at
http://127.0.0.1:8337/aura/tracks/3.

**Note the absence of a trailing slash**:
http://127.0.0.1:8337/aura/tracks/3/ returns a "404 Not Found" error.


Configuration
=============

To configure the plugin, make an "aura:" section in your configuration
file. The available options are:

* **host**: The server hostname. Set this to "0.0.0.0" to bind to all
  interfaces. Default: "127.0.0.1".

* **port**: The server port. Default: "8337".

* **cors**: A YAML list of origins to allow CORS requests from (see
  Cross-Origin Resource Sharing (CORS), below). Default: disabled.

* **cors_supports_credentials**: Allow authenticated requests when
  using CORS. Default: disabled.

* **page_limit**: The number of items responses should be truncated to
  if the client does not specify. Default "500".


Cross-Origin Resource Sharing (CORS)
====================================

CORS allows browser clients to make requests to the AURA server. You
should set the "cors" configuration option to a YAML list of allowed
origins.

For example:

   aura:
       cors:
           - http://www.example.com
           - https://aura.example.org

In order to use the plugin with a local browser client accessed using
"file:///" you must include "'null'" in the list of allowed origins
(including quote marks):

   aura:
       cors:
           - 'null'

Alternatively you use "'*'" to enable access from all origins. Note
that there are security implications if you set the origin to "'*'",
so please research this before using it. Note the use of quote marks
when allowing all origins.

If the server is behind a proxy that uses credentials, you might want
to set the "cors_supports_credentials" configuration option to true to
let in-browser clients log in. Note that this option has not been
tested, so it may not work.


Using an External WSGI Server
=============================

If you would like to use a different WSGI server (not Flask's built-in
one), then you can! The "beetsplug.aura" module provides a WSGI
callable called "create_app()" which can be used by many WSGI servers.

For example to run the AURA server using gunicorn use "gunicorn
'beetsplug.aura:create_app()'", or for uWSGI use "uwsgi --http :8337
--module 'beetsplug.aura:create_app()'". Note that these commands just
show how to use the AURA app and you would probably use something a
bit different in a production environment. Read the relevant server's
documentation to figure out what you need.


Reverse Proxy Support
=====================

The plugin should work behind a reverse proxy without further
configuration, however this has not been tested extensively. For
details of what headers must be rewritten and a sample NGINX
configuration see Flask proxy setups.

It is (reportedly) possible to run the application under a URL prefix
(for example so you could have "/foo/aura/server" rather than
"/aura/server"), but you'll have to work it out for yourself :-)

If using NGINX, do **not** add a trailing slash ("/") to the URL where
the application is running, otherwise you will get a 404. However if
you are using Apache then you **should** add a trailing slash.


Issues
======

As of writing there are some differences between the specification and
this implementation:

* Compound filters are not specified in AURA, but this server
  interprets multiple "filter" parameters as AND. See issue #19 for
  discussion.

* The "bitrate" parameter used for content negotiation is not
  supported. Adding support for this is doable, but the way Flask
  handles acceptable MIME types means it's a lot easier not to bother
  with it. This means an error could be returned even if no
  transcoding was required.

It is possible that some attributes required by AURA could be absent
from the server's response if beets does not have a saved value for
them. However, this has not happened so far.

Beets fields (including flexible fields) that do not have an AURA
equivalent are not provided in any resource's attributes section,
however these fields may be used for filtering.

The "mimetype" and "framecount" attributes for track resources are not
supported. The first is due to beets storing the file type (e.g.
"MP3"), so it is hard to filter by MIME type. The second is because
there is no corresponding beets field.

Artists are defined by the "artist" field on beets Items, which means
some albums have no "artists" relationship. Albums only have related
artists when their beets "albumartist" field is the same as the
"artist" field on at least one of it's constituent tracks.

The only art tracked by beets is a single cover image, so only albums
have related images at the moment. This could be expanded to looking
in the same directory for other images, and relating tracks to their
album's image.

There are likely to be some performance issues, especially with larger
libraries. Sorting, pagination and inclusion (most notably of images)
are probably the main offenders. On a related note, the program
attempts to import Pillow every time it constructs an image resource
object, which is not good.

The beets library is accessed using a so called private function (with
a single leading underscore) "beets.ui.__init__._open_library()". This
shouldn't cause any issues but it is probably not best practice.
