Fureon is a way to build and listen to a music library with friends. Heavily inspired by plug.dj, r-a-dio, and Soundcloud, it sets up a server where users contribute to build a global song library and can listen in on a main global music stream. Users can request songs from the library to be played on the main stream to share with others tuning in. Alternatively, they can freely stream and listen to any of the songs in the library.
Fureon has three main external dependencies: mpd/mpc icecast2 redis
Install and set up mpd/mpc to work as a source for icecast2. Here's a concise guide on how to set up mpd/mpc to stream with icecast2.
redis should typically be available in the repos for most linux distros.
After that, the backend can be quickly installed using setuptools with the provided setup.py. In /backend create and source a virtualenv then execute setup.py with install or develop:
$ python ./setup.py install
All python dependencies should be covered by the install. Several parameters in /backend/fureon/config.py need to be set before the app can run.
Fureon needs to know where to look for songs and where to save album art.
paths = {
'song_directory' : '/home/user/music/',
'static_folder_path' : '/var/www/fureon.site.com/frontend/static/',
'log_file' : os.path.join(PARENT_DIRECTORY, 'fureon.log'
}
song_directory needs to be set to the same directory as mpd's song directory. static_folder_path needs should be set to where static files intend to be served. This is used as the target location to save album art. Leaving it blank will have the app not save any album art.
Fureon uses SQLAlchemy which requires a database connection. The connection parameters must be set for the database used. An example for PostgreSQL:
database = {
'drivername' : 'postgres',
'host' : 'localhost',
'port' : '5432',
'username' : 'myusername',
'password' : 'supersecretpassword',
'database' : 'fureon',
}
Or for a simple SQLite db:
database = {
'drivername' : 'sqlite',
'host' : '',
'port' : '',
'username' : '',
'password' : '',
'database' : 'fureon.db',
}
Fureon uses redis to keep track of the delay for song and user request blocking.
cache = {
'host' : 'localhost',
'port' : '6379',
}
These are the default redis connection parameters. Change them if the system's redis server is set differently.
Once all the configuration is done, run the app with:
$ fureon-start
The Fureon backend runs on tornado + SQLAlchemy + redis. Tornado was chosen for its performance, modularity and light weight, as well as its ease in implementing websockets. The database is abstracted into models with SQLAlchemy and redis is used as both a cache as well as a means to have a data structure that can expire with time. Fureon shares the music db directory of the system's mpd installation and can scan the directory while also extracting the metadata with mutagen into the database models.
Fureon currently handles playing music to the main stream with mpd controlled by mpc. The mpd output is used as an icecast source to serve listeners. The app watches any changes to mpd with the mpc idle cli command, and upon events such as a transition to the next song, it adds a callback to the tornado ioloop which signals changes to the fureon db playlist which then sends commands back to mpc to update the mpd playlist keeping both the mpd and fureon db playlists consistent.
Many functions in the Fureon backend can be accessed from it's RESTlike API. This includes searching for songs, getting the current playlist, getting song data, as well as requesting songs.
Frontend work will be done in cooperation with github user Rifu working on the anzu repo. Powered by Ember.js, it will request the necessary data from the backend's RESTlike endpoints to build the app UI. The projects will merge eventually once a final name is settled and a good merging point is reached.
The Fureon backend API has the following endpoints:
$ /api
Returns this description of all api endpoints in JSON format.
$ /api/playlist
Returns the current playlist with the order given as keys. Includes: song title, song artist, song id, song duration, and whether the song was user requested or not.
$ /api/stream_endpoint
Returns the uri for the stream endpoint.
$ /api/song/find?song-id=<song_id>
Returns the full details of a single song with the id <song_id>. Data includes (if available): id, title, artist, album, trackno, date, genre, duration, file_path, art_path, datetime_added, tags, play_count, fave_count.
$ /api/album/find?name=<album_name>
An exact search for albums with the name <album_name>. Will return a numbered object with song titles, song artists, song_ids, tracknos, dates, fave_count, play_count, and art_paths.
$ /api/artist?name=<artist_name>
An exact search for albums with the name <artist_name>. Will return a numbered object with song titles, song_ids, album names, dates, play_count, fave_count.
$ /api/request_song
POST method with params: song-id=<song_id>. Sends a song request to update the main stream playlist with the requested song. User requested songs have higher priority than random songs. Stream playlist order is requested songs in added order, then random songs in added order.
Users should only be able to request a certain amount of times within a timespan. This is to prevent a single user from hijacking the stream with requests. Implement with redis in the same way the song request blocking works but with the requester's IP instead of song_id.
This is for the user to login in order to mark songs as favorites and accessing a favorites page. Will require adding a user model as well as an authentication system. Tornado has authentication built in and storing passwords will most likely be done with passlib.
Validate the arguments to an API request and return useful error responses if the request is incorrect.
Besides the main stream, users should be able to listen to any track that's on the database separately.
Whenever another user sends a request or a song ends, the playlist order changes. Use websockets to keep a constant connection and send the user playlist change events. Implementation will most likely be done with sockjs-tornad
A way to control the stream by sending commands to an IRC bot. Looking to add a plugin for kochira