We are no longer offering accounts on this server. Consider https://gitlab.freedesktop.org/ as a place to host projects.

Commit 26714531 authored by Jonas Haraldsson's avatar Jonas Haraldsson

Merge commit 'refs/merge-requests/54' of git://gitorious.org/foocorp/gnu-fm into merge-requests/54

parents 18bc2f61 0332ff89
......@@ -88,7 +88,7 @@ Installing Smarty:
$ tar zxvf Smarty*
$ tar zxvf smarty*
$ mv smarty/libs/* smarty/.
$ mv smarty-gettext/block.t.php smarty/.
$ mv smarty-gettext/block.t.php smarty/plugins/.
Installing ADOdb:
$ cp external_dependencies/adodb511.tgz /var/www/htdocs/nixtape/.
......
......@@ -30,10 +30,10 @@ if (!isset($config_version) || $config_version != $version) {
die('Configuration file is out of date. Please delete it (and associated database) and <a href="install.php">reinstall</a>.'); //TODO: Upgrade script for release versions
}
if (isset($_GET['hs']) && isset($_GET['p'])) {
if (substr($_GET['p'], 0, 3) == '1.2') {
if (isset($_REQUEST['hs']) && isset($_REQUEST['p'])) {
if (substr($_REQUEST['p'], 0, 3) == '1.2') {
require_once('submissions/1.2/handshake.php');
} else if (substr($_GET['p'], 0, 3) == '1.1') {
} else if (substr($_REQUEST['p'], 0, 3) == '1.1') {
require_once('submissions/1.1/handshake.php');
}
} else {
......
......@@ -30,11 +30,11 @@ require_once('temp-utils.php');
$supported_protocols = array('1.1');
if (!isset($_GET['p']) || !isset($_GET['u']) || !isset($_GET['c'])) {
if (!isset($_REQUEST['p']) || !isset($_REQUEST['u']) || !isset($_REQUEST['c'])) {
die("FAILED\n");
}
$protocol = $_GET['p']; $username = $_GET['u']; $client = $_GET['c'];
$protocol = $_REQUEST['p']; $username = $_REQUEST['u']; $client = $_REQUEST['c'];
if (!in_array($protocol, $supported_protocols)) {
die("FAILED Unsupported protocol version\n");
......
......@@ -26,15 +26,15 @@ require_once('temp-utils.php');
$supported_protocols = array('1.2', '1.2.1');
if (!isset($_GET['p']) || !isset($_GET['u']) || !isset($_GET['t']) || !isset($_GET['a']) || !isset($_GET['c'])) {
if (!isset($_REQUEST['p']) || !isset($_REQUEST['u']) || !isset($_REQUEST['t']) || !isset($_REQUEST['a']) || !isset($_REQUEST['c'])) {
die("BADAUTH\n");
}
$protocol = $_GET['p'];
$username = $_GET['u'];
$timestamp = $_GET['t'];
$auth_token = $_GET['a'];
$client = $_GET['c'];
$protocol = $_REQUEST['p'];
$username = $_REQUEST['u'];
$timestamp = $_REQUEST['t'];
$auth_token = $_REQUEST['a'];
$client = $_REQUEST['c'];
if ($client == 'import') {
die("FAILED Import scripts are broken\n"); // this should be removed or changed to check the version once import.php is fixed
......@@ -48,8 +48,8 @@ if (abs($timestamp - time()) > 300) {
die("BADTIME\n"); // let's try a 5-minute tolerance
}
if (isset($_GET['api_key']) && isset($_GET['sk'])) {
$authed = check_web_auth($username, $auth_token, $timestamp, $_GET['api_key'], $_GET['sk']);
if (isset($_REQUEST['api_key']) && isset($_REQUEST['sk'])) {
$authed = check_web_auth($username, $auth_token, $timestamp, $_REQUEST['api_key'], $_REQUEST['sk']);
} else {
$authed = check_standard_auth($username, $auth_token, $timestamp);
}
......
......@@ -137,7 +137,7 @@ $method_map = array(
* @api
*/
function method_user_getTopArtists() {
if (!isset($_GET['user'])) {
if (!isset($_REQUEST['user'])) {
report_failure(LFM_INVALID_PARAMS);
}
......@@ -147,7 +147,7 @@ function method_user_getTopArtists() {
$streamable = get_with_default('streamable', 0);
$cache = 600;
$xml = UserXML::getTopArtists($_GET['user'], $limit, $streamable, $page, $period, $cache);
$xml = UserXML::getTopArtists($_REQUEST['user'], $limit, $streamable, $page, $period, $cache);
respond($xml);
}
......@@ -170,14 +170,14 @@ function method_user_getTopArtists() {
* @api
*/
function method_user_getRecentTracks() {
if (!isset($_GET['user'])) {
if (!isset($_REQUEST['user'])) {
report_failure(LFM_INVALID_PARAMS);
}
$limit = get_with_default('limit', 50);
$page = get_with_default('page', 1);
$xml = UserXML::getRecentTracks($_GET['user'], $limit, $page);
$xml = UserXML::getRecentTracks($_REQUEST['user'], $limit, $page);
respond($xml);
}
......@@ -198,7 +198,7 @@ function method_user_getRecentTracks() {
* @api
*/
function method_user_getTopTags() {
if (!isset($_GET['user'])) {
if (!isset($_REQUEST['user'])) {
report_failure(LFM_INVALID_PARAMS);
}
......@@ -206,7 +206,7 @@ function method_user_getTopTags() {
$cache = 600;
$xml = UserXML::getTopTags($_GET['user'], $limit, $cache);
$xml = UserXML::getTopTags($_REQUEST['user'], $limit, $cache);
respond($xml);
}
......@@ -231,7 +231,7 @@ function method_user_getTopTags() {
* @api
*/
function method_user_getPersonalTags() {
if(!isset($_GET['user']) or !isset($_GET['tag']) or !isset($_GET['taggingtype'])) {
if(!isset($_REQUEST['user']) || !isset($_REQUEST['tag']) || !isset($_REQUEST['taggingtype'])) {
report_failure(LFM_INVALID_PARAMS);
}
......@@ -241,7 +241,7 @@ function method_user_getPersonalTags() {
$streamable = False;
$cache = 600;
$xml = UserXML::getPersonalTags($_GET['user'], $_GET['tag'], $_GET['taggingtype'], $limit, $page, $cache, $streamable);
$xml = UserXML::getPersonalTags($_REQUEST['user'], $_REQUEST['tag'], $_REQUEST['taggingtype'], $limit, $page, $cache, $streamable);
respond($xml);
}
......@@ -262,13 +262,13 @@ function method_user_getPersonalTags() {
* @api
*/
function method_user_getTagInfo() {
if(!isset($_GET['user']) or !isset($_GET['tag'])) {
if(!isset($_REQUEST['user']) || !isset($_REQUEST['tag'])) {
report_failure(LFM_INVALID_PARAMS);
}
$cache = 600;
$xml = UserXML::getTagInfo($_GET['user'], $_GET['tag'], $cache);
$xml = UserXML::getTagInfo($_REQUEST['user'], $_REQUEST['tag'], $cache);
respond($xml);
}
......@@ -292,7 +292,7 @@ function method_user_getTagInfo() {
* @api
*/
function method_user_getTopTracks() {
if (!isset($_GET['user'])) {
if (!isset($_REQUEST['user'])) {
report_failure(LFM_INVALID_PARAMS);
}
$limit = get_with_default('limit', 10);
......@@ -301,7 +301,7 @@ function method_user_getTopTracks() {
$period = get_with_default('period', 'overall');
$cache = 600;
$xml = UserXML::getTopTracks($_GET['user'], $limit, $streamable, $page, $period, $cache);
$xml = UserXML::getTopTracks($_REQUEST['user'], $limit, $streamable, $page, $period, $cache);
respond($xml);
}
......@@ -320,11 +320,11 @@ function method_user_getTopTracks() {
* @api
*/
function method_user_getInfo() {
if (!isset($_GET['user'])) {
if (!isset($_REQUEST['user'])) {
report_failure(LFM_INVALID_PARAMS);
}
$xml = UserXML::getInfo($_GET['user']);
$xml = UserXML::getInfo($_REQUEST['user']);
respond($xml);
}
......@@ -347,11 +347,11 @@ function method_user_getInfo() {
* @api
*/
function method_user_getLovedTracks() {
if (!isset($_GET['user'])) {
if (!isset($_REQUEST['user'])) {
report_failure(LFM_INVALID_PARAMS);
}
$user = $_GET['user'];
$user = $_REQUEST['user'];
$limit = get_with_default('limit', 50);
$page = get_with_default('page', 1);
......@@ -380,11 +380,11 @@ function method_user_getLovedTracks() {
* @api
*/
function method_user_getBannedTracks() {
if (!isset($_GET['user'])) {
if (!isset($_REQUEST['user'])) {
report_failure(LFM_INVALID_PARAMS);
}
$user = $_GET['user'];
$user = $_REQUEST['user'];
$limit = get_with_default('limit', 50);
$page = get_with_default('page', 1);
......@@ -409,11 +409,11 @@ function method_user_getBannedTracks() {
* @api
*/
function method_user_getNeighbours() {
if (!isset($_GET['user'])) {
if (!isset($_REQUEST['user'])) {
report_failure(LFM_INVALID_PARAMS);
}
$user = $_GET['user'];
$user = $_REQUEST['user'];
$limit = get_with_default('limit', 50);
$xml = UserXML::getNeighbours($user, $limit);
......@@ -467,11 +467,11 @@ function method_artist_addTags() {
* @api
*/
function method_artist_getInfo() {
if (!isset($_GET['artist'])) {
if (!isset($_REQUEST['artist'])) {
report_failure(LFM_INVALID_PARAMS);
}
$xml = ArtistXML::getInfo($_GET['artist']);
$xml = ArtistXML::getInfo($_REQUEST['artist']);
respond($xml);
}
......@@ -494,7 +494,7 @@ function method_artist_getInfo() {
* @api
*/
function method_artist_getTopTracks() {
if (!isset($_GET['artist'])) {
if (!isset($_REQUEST['artist'])) {
report_failure(LFM_INVALID_PARAMS);
}
$limit = get_with_default('limit', 50);
......@@ -502,7 +502,7 @@ function method_artist_getTopTracks() {
$streamable = get_with_default('streamable', False);
$cache = 600;
$xml = ArtistXML::getTopTracks($_GET['artist'], $limit, $streamable, $page, $cache);
$xml = ArtistXML::getTopTracks($_REQUEST['artist'], $limit, $streamable, $page, $cache);
respond($xml);
}
......@@ -523,7 +523,7 @@ function method_artist_getTopTracks() {
* @api
*/
function method_artist_getTopTags() {
if (!isset($_GET['artist'])) {
if (!isset($_REQUEST['artist'])) {
report_failure(LFM_INVALID_PARAMS);
}
......@@ -531,7 +531,7 @@ function method_artist_getTopTags() {
$cache = 600;
$xml = ArtistXML::getTopTags($_GET['artist'], $limit, $cache);
$xml = ArtistXML::getTopTags($_REQUEST['artist'], $limit, $cache);
respond($xml);
}
......@@ -586,11 +586,11 @@ function method_artist_getTags() {
* @api
*/
function method_artist_getFlattr() {
if (!isset($_GET['artist'])) {
if (!isset($_REQUEST['artist'])) {
report_failure(LFM_INVALID_PARAMS);
}
$xml = ArtistXML::getFlattr($_GET['artist']);
$xml = ArtistXML::getFlattr($_REQUEST['artist']);
respond($xml);
}
......@@ -645,7 +645,7 @@ function method_album_addTags() {
* @api
*/
function method_album_getTopTags() {
if (!isset($_GET['artist']) || !isset($_GET['album'])) {
if (!isset($_REQUEST['artist']) || !isset($_REQUEST['album'])) {
report_failure(LFM_INVALID_PARAMS);
}
......@@ -653,7 +653,7 @@ function method_album_getTopTags() {
$cache = 600;
$xml = AlbumXML::getTopTags($_GET['artist'], $_GET['album'], $limit, $cache);
$xml = AlbumXML::getTopTags($_REQUEST['artist'], $_REQUEST['album'], $limit, $cache);
respond($xml);
}
......@@ -681,7 +681,7 @@ function method_album_getTopTags() {
* @todo Only require sk if no user specified, see http://www.last.fm/api/show/album.getTags.
*/
function method_album_getTags() {
if (!isset($_GET['artist']) || !isset($_GET['album'])) {
if (!isset($_REQUEST['artist']) || !isset($_REQUEST['album'])) {
report_failure(LFM_INVALID_PARAMS);
}
......@@ -690,7 +690,7 @@ function method_album_getTags() {
$userid = get_userid();
$cache = 600;
$xml = AlbumXML::getTags($_GET['artist'], $_GET['album'], $userid, $limit, $cache);
$xml = AlbumXML::getTags($_REQUEST['artist'], $_REQUEST['album'], $userid, $limit, $cache);
respond($xml);
}
......@@ -732,10 +732,13 @@ function method_auth_getToken() {
* md5(username+md5(password))
*
* ###Parameters
* * **username** (required) : Name of the user
* * **authtoken** (required) : Authentication token
* * **format** (optional) : Format of response, **xml** or **json**. Default is xml.
* - - -
*
* @todo parameter 'authtoken' is deprecated on last.fm's version, uses parameter 'password' with HTTPS and POST instead
* @todo make XML response better (use xml_response)
* @package Webservice
* @subpackage Auth
* @api
......@@ -743,14 +746,16 @@ function method_auth_getToken() {
function method_auth_getMobileSession() {
global $adodb;
if (!isset($_GET['authToken'])) {
report_failure(LFM_INVALID_TOKEN);
$_REQUEST_lower = array_change_key_case($_REQUEST, CASE_LOWER);
if (!isset($_REQUEST_lower['authtoken']) || !isset($_REQUEST['username'])) {
report_failure(LFM_INVALID_PARAMS);
}
// Check for a token that is bound to a user
try {
$result = $adodb->GetRow('SELECT username, lower(username) AS lc_username, password FROM Users WHERE '
. 'lower(username) = lower(' . $adodb->qstr($_GET['username']) . ')');
. 'lower(username) = lower(' . $adodb->qstr($_REQUEST['username']) . ')');
} catch (Exception $e) {
report_failure(LFM_SERVICE_OFFLINE);
}
......@@ -761,7 +766,7 @@ function method_auth_getMobileSession() {
$username = $result['username'];
$lc_username = $result['lc_username'];
$password = $result['password'];
if (md5($lc_username . $password) != $_GET['authToken']) {
if (md5($lc_username . $password) != $_REQUEST_lower['authtoken']) {
report_failure(LFM_INVALID_TOKEN);
}
......@@ -806,6 +811,7 @@ function method_auth_getMobileSession() {
* * **format** (optional) : Format of response, **xml** or **json**. Default is xml.
* - - -
* @todo Documentation
* @todo make XML response better (use xml_response)
* @package Webservice
* @subpackage Auth
* @api
......@@ -813,14 +819,14 @@ function method_auth_getMobileSession() {
function method_auth_getSession() {
global $adodb;
if (!isset($_GET['token'])) {
if (!isset($_REQUEST['token'])) {
report_failure(LFM_INVALID_TOKEN);
}
// Check for a token that (1) is bound to a user, and (2) is not bound to a session
try {
$username = $adodb->GetOne('SELECT username FROM Auth WHERE '
. 'token = ' . $adodb->qstr($_GET['token']) . ' AND '
. 'token = ' . $adodb->qstr($_REQUEST['token']) . ' AND '
. 'username IS NOT NULL AND sk IS NULL');
} catch (Exception $e) {
report_failure(LFM_SERVICE_OFFLINE);
......@@ -835,7 +841,7 @@ function method_auth_getSession() {
try {
$result = $adodb->Execute('UPDATE Auth SET '
. 'sk = ' . $adodb->qstr($session) . ' WHERE '
. 'token = ' . $adodb->qstr($_GET['token']));
. 'token = ' . $adodb->qstr($_REQUEST['token']));
} catch (Exception $e) {
report_failure(LFM_SERVICE_OFFLINE);
}
......@@ -871,6 +877,7 @@ function method_auth_getSession() {
* **HTTP request method** : POST.
* - - -
*
* @todo make XML response better (use xml_response)
* @package Webservice
* @subpackage Radio
* @api
......@@ -1047,7 +1054,7 @@ function method_track_addTags() {
* @api
*/
function method_track_getTopTags() {
if (!isset($_GET['artist']) || !isset($_GET['track'])) {
if (!isset($_REQUEST['artist']) || !isset($_REQUEST['track'])) {
report_failure(LFM_INVALID_PARAMS);
}
......@@ -1055,7 +1062,7 @@ function method_track_getTopTags() {
$cache = 600;
$xml = TrackXML::getTopTags($_GET['artist'], $_GET['track'], $limit, $cache);
$xml = TrackXML::getTopTags($_REQUEST['artist'], $_REQUEST['track'], $limit, $cache);
respond($xml);
}
......@@ -1083,7 +1090,7 @@ function method_track_getTopTags() {
* @todo Only require sk if no user specified, see http://www.last.fm/api/show/track.getTags.
*/
function method_track_getTags() {
if (!isset($_GET['artist']) || !isset($_GET['track'])) {
if (!isset($_REQUEST['artist']) || !isset($_REQUEST['track'])) {
report_failure(LFM_INVALID_PARAMS);
}
......@@ -1092,7 +1099,7 @@ function method_track_getTags() {
$cache = 600;
$userid = get_userid();
$xml = TrackXML::getTags($_GET['artist'], $_GET['track'], $userid, $limit, $cache);
$xml = TrackXML::getTags($_REQUEST['artist'], $_REQUEST['track'], $userid, $limit, $cache);
respond($xml);
}
......@@ -1266,7 +1273,7 @@ function method_tag_getTopTags() {
* @api
*/
function method_tag_getTopArtists() {
if (!isset($_GET['tag'])) {
if (!isset($_REQUEST['tag'])) {
report_failure(LFM_INVALID_PARAMS);
}
......@@ -1276,7 +1283,7 @@ function method_tag_getTopArtists() {
$streamable = True;
$cache = 600;
$xml = TagXML::getTopArtists($_GET['tag'], $limit, $page, $streamable, $cache);
$xml = TagXML::getTopArtists($_REQUEST['tag'], $limit, $page, $streamable, $cache);
respond($xml);
}
......@@ -1298,7 +1305,7 @@ function method_tag_getTopArtists() {
* @api
*/
function method_tag_getTopAlbums() {
if (!isset($_GET['tag'])) {
if (!isset($_REQUEST['tag'])) {
report_failure(LFM_INVALID_PARAMS);
}
......@@ -1308,7 +1315,7 @@ function method_tag_getTopAlbums() {
$streamable = True;
$cache = 600;
$xml = TagXML::getTopAlbums($_GET['tag'], $limit, $page, $streamable, $cache);
$xml = TagXML::getTopAlbums($_REQUEST['tag'], $limit, $page, $streamable, $cache);
respond($xml);
}
......@@ -1330,7 +1337,7 @@ function method_tag_getTopAlbums() {
* @api
*/
function method_tag_getTopTracks() {
if (!isset($_GET['tag'])) {
if (!isset($_REQUEST['tag'])) {
report_failure(LFM_INVALID_PARAMS);
}
......@@ -1340,7 +1347,7 @@ function method_tag_getTopTracks() {
$streamable = True;
$cache = 600;
$xml = TagXML::getTopTracks($_GET['tag'], $limit, $page, $streamable, $cache);
$xml = TagXML::getTopTracks($_REQUEST['tag'], $limit, $page, $streamable, $cache);
respond($xml);
}
......@@ -1360,13 +1367,13 @@ function method_tag_getTopTracks() {
* @api
*/
function method_tag_getInfo() {
if (!isset($_GET['tag'])) {
if (!isset($_REQUEST['tag'])) {
report_failure(LFM_INVALID_PARAMS);
}
$cache = 600;
$xml = TagXML::getInfo($_GET['tag'], $cache);
$xml = TagXML::getInfo($_REQUEST['tag'], $cache);
respond($xml);
}
......@@ -1406,8 +1413,7 @@ function report_failure($code) {
$json_data = array('error' => $code, 'message' => $error_text[$code]);
json_response(json_encode($json_data));
} else {
print("<lfm status=\"failed\">\n");
print(" <error code=\"{$code}\">" . $error_text[$code] . "</error></lfm>");
xml_response(XML::error('failed', $code, $error_text[$code]));
}
die();
}
......@@ -1435,8 +1441,8 @@ function json_response($data) {
}
function get_with_default($param, $default) {
if (isset($_GET[$param])) {
return $_GET[$param];
if (isset($_REQUEST[$param])) {
return $_REQUEST[$param];
} else {
return $default;
}
......
......@@ -31,6 +31,7 @@ require_once($install_path . '/utils/resolve-external.php');
require_once($install_path . '/utils/licenses.php');
require_once($install_path . '/utils/rewrite-encode.php');
require_once($install_path . '/temp-utils.php'); // this is extremely dodgy and shameful
require_once($install_path . '/data/clientcodes.php');
/**
* Provides access to server-wide data
......@@ -452,16 +453,11 @@ class Server {
n.track,
n.album,
client,
ClientCodes.name,
ClientCodes.url,
ClientCodes.free,
n.mbid,
t.license
FROM Now_Playing n
LEFT OUTER JOIN Scrobble_Sessions ss
ON n.sessionid=ss.sessionid
LEFT OUTER JOIN ClientCodes
ON ss.client=ClientCodes.code
LEFT OUTER JOIN Track t
ON lower(n.artist) = lower(t.artist_name)
AND lower(n.album) = lower(t.album_name)
......@@ -476,16 +472,11 @@ class Server {
n.track,
n.album,
client,
ClientCodes.name,
ClientCodes.url,
ClientCodes.free,
n.mbid,
t.license
FROM Now_Playing n
LEFT OUTER JOIN Scrobble_Sessions ss
ON n.sessionid=ss.sessionid
LEFT OUTER JOIN ClientCodes
ON ss.client=ClientCodes.code
LEFT OUTER JOIN Track t
ON lower(n.artist) = lower(t.artist_name)
AND lower(n.album) = lower(t.album_name)
......@@ -501,15 +492,12 @@ class Server {
foreach ($data as &$i) {
$row = sanitize($i);
// this logic should be cleaned up and the free/nonfree decision be moved into the smarty templates
if ($row['name'] == '') {
$clientstr = strip_tags(stripslashes($row['client'])) . ' (unknown, <a href="http://ideas.libre.fm/index.php/Client_Codes">please tell us what this is</a>)';
} else if ($row['free'] == 'Y') {
$clientstr = '<a href="' . strip_tags(stripslashes($row['url'])) . '">' . strip_tags(stripslashes($row['name'])) . '</a>';
} else {
$clientstr = '<a href="http://en.wikipedia.org/wiki/Category:Free_media_players">' . strip_tags(stripslashes($row['name'])) . '</a>';
$client = getClientData($row['client']);
if(is_array($client)) {
$row['clientname'] = $client['name'];
$row['clienturl'] = $client['url'];
$row['clientfree'] = $client['free'];
}
$row['clientstr'] = $clientstr;
$row['username'] = uniqueid_to_username($row['userid']);
$row['userurl'] = Server::getUserURL($row['username']);
$row['artisturl'] = Server::getArtistURL($row['artist']);
......
<?php
/* GNU FM -- a free network service for sharing your music listening habits
Copyright (C) 2013 Free Software Foundation, Inc
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Get name, url and license type of a scrobble client based on client code
*
* @param string $code Client code
* @return array ("name" => string, "url" => string, "free" => boolean)
*/
function getClientData($code) {
global $base_url, $site_name;
$clients = array(
"amk" => array("name" => "Amarok 1.4.x plugin", "url" => "http://amarok.kde.org/", "free" => True),
"amp" => array("name" => "Aimp2", "url" => "http://www.aimp.ru/", "free" => False),
"amy" => array("name" => "Anomaly", "url" => "https://chrome.google.com/webstore/detail/ajbkmdgmhbjjhephmgbpgialfbnjbmkb", "free" => True),
"ark" => array("name" => "Amarok", "url" => "http://amarok.kde.org/", "free" => True),
"ass" => array("name" => "Last.fm player", "url" => "http://www.last.fm/download", "free" => True),
"atu" => array("name" => "aTunes", "url" => "http://www.atunes.org/", "free" => True),
"aud" => array("name" => "Audacious", "url" => "http://audacious-media-player.org/", "free" => True),
"bil" => array("name" => "billy", "url" => "http://www.sheepfriends.com/?page=billy", "free" => False),
"blu" => array("name" => "Bluemindo", "url" => "http://bluemindo.codingteam.net/", "free" => True),
"bsh" => array("name" => "Banshee", "url" => "http://banshee-project.org/", "free" => True),
"bwt" => array("name" => "Bowtie", "url" => "http://www.bowtieapp.com/", "free" => False),
"cmu" => array("name" => "CmuScrobbler", "url" => "http://n.ethz.ch/%7edflatz/cmuscrobbler/", "free" => True),
"cpl" => array("name" => "cplay scrobbler", "url" => "http://sebi.tla.ro/cplay_scrobbler", "free" => True),
"cub" => array("name" => "Cuberok", "url" => "http://code.google.com/p/cuberok/", "free" => True),
"dbl" => array("name" => "Decibel Audio Player", "url" => "http://decibel.silent-blade.org/", "free" => True),
"ddb" => array("name" => "DeaDBeeF", "url" => "http://deadbeef.sourceforge.net/", "free" => True),
"dms" => array("name" => "donky mpdscrob", "url" => "http://github.com/mjhayes/donky/tree/master", "free" => True),
"ems" => array("name" => "EMMS", "url" => "http://www.gnu.org/software/emms/", "free" => True),
"exa" => array("name" => "Exaile", "url" => "http://www.exaile.org/", "free" => True),
"foo" => array("name" => "foobar2000", "url" => "http://www.foobar2000.org/", "free" => True),
"gmb" => array("name" => "gmusicbrowser", "url" => "http://gmusicbrowser.org/", "free" => True),
"gmm" => array("name" => "Goggles Music Manager", "url" => "http://code.google.com/p/gogglesmm/"