Hello again!

In this post I’ll describe how to use the WebService::Freesound Perl module in a simple web app. All files are available on GitHub to download and have a look at.

About Freesound.org

Freesound.org is “… a collaborative database of Creative Commons Licensed sounds. Browse, download and share sounds…” – it has an API which needs OAuth2 authentication. With the API you can do complex searches which will return all kinds of sample types, along with pictures of the waveforms, preview sounds, licenses, and more. You can download the sample with the API too.

About OAuth2 and Freesound

This is a multi-step dance described on Freesound here and Officially at https://tools.ietf.org/html/rfc6749. Another description on StackOverflow involves telling the tale of a baker called Olaf, which is by far the best way to get your head round it for the first time.

So WebService::Freesound is a wrapper for the OAuth2 API interface and then provides basic search and download functions (no upload yet, as I suspect that would be much easier on the website).

I’ve knocked up a basic web app (on GitHub) to show how WebService::Freesound can be used with JavaScript, Template::Toolkit, some HTML5 and some CSS3.

Read on …

Sign up to Freesound

If you’ve read about Olaf and the Freesound Authentication page, then you’ll have an idea what WebService::Freesound should do. You’ll have to sign up with Feesound.org and then request access to the API to try all this out :

  1. Sign up : https://www.freesound.org/home/register/
  2. Apply for API access : https://www.freesound.org/apiv2/apply

You’ll get a client_id (public) and a client_secret (not public).

About WebService::Freesound

Now, if you’ve installed WebService::Freesound then you can start with the main CGI (in my case, something called elemenski.cgi, for reasons I have forgotten).

elemenski.cgi:

#!/usr/bin/perl
use WebService::Freesound;
my %args = (
           client_id     => 'your client_id',
           client_secret => 'your client_secret',
           session_file  => '/var/www/elementski/sessions/freesoundrc',
        );
my $freesound = WebService::Freesound->new (%args);
my $authorization_url = $freesound->get_authorization_url();

And then send the $authorization_url to the Template::Toolkit :

[% IF authorization_url %]
<iframe src=[% authorization_url %]>No iframe support</iframe>
[% END %]

This will then display a Freesound Authorise/Deny panel where the user can authorise your app to use his/her Freesound account to search and download samples :

freesound-api-login

freesound-api-request-permission

When the user clicks on Authorize!, then the Callback URL is invoked. You’ll need to set this on Freesound.org : log in and go to https://www.freesound.org/apiv2/apply and click the edit button – there will be a ‘Callback URL’ field to fill in. I’ve set it to : https://caesuramedia.org/cgi-bin/elementski/callback.cgi.

In callback.cgiwe are given the OAuth2 ‘code’ which we can pick up using good ol’ CGI and send it to $freesound->get_new_oauth_tokens

callback.cgi:

my $cgi_query = CGI->new;
my %args = (
    client_id     => 'your client_id',
    client_secret => 'your client_secret',
    session_file  => '/var/www/elementski/sessions/freesoundrc',
);
my $freesound = WebService::Freesound->new(%args);
if ( $cgi_query->param('code') ) {
    my $rc = $freesound->get_new_oauth_tokens( $cgi_query->param('code') );
    if ($rc) {
        print "Authorisation ok, now use the search box";
    }
    else {
        print $freesound->error;
    }
}
elsif ( $cgi_query->param('error') ) {
    print $cgi_query->param('error') . " : refresh page to try again.";
}
else {
    print "Not Authorised";
}

So WebService::Freesound $freesound->get_new_oauth_tokens converts this ‘code’ into access tokens and stores them in your session_file. Best to keep the session file with limited permissions then.

From there on in, it should be transparent. In the main CGI (elementski, say) you can check authorisation before doing a search, and with the refresh_if_expired param set it will refresh the tokens if expired :

elemenski.cgi:

my $rc = $freesound->check_authority( refresh_if_expired => 1 );
if ( !$rc ) {
    # gets sent to Toolkit::Template to re-show the 
    # Freesound.org authority panel :
    $get_authorisation = $freesound->get_authorization_url(); 
}
my $response = $freesound->query("query=...");

$response is an HTTP::Response object where you can extract the data you’ve searched for. Freesound.org allows XML and json formats.

In order to test your app, then you can go to https://www.freesound.org/home/app_permissions/ and revoke permissions in order to bring up the Freesound Authorize iframe again.

Download

As a special feature to integrate with JavaScript, the $freesound->download method provides a counter_file option which is a plain file containing the progress of the download :

download.cgi:

...
my $rc = $freesound->check_authority ( refresh_if_expired => 1 );
...
my $file = $freesound->download( 
   $id, 
   '/var/www/elementski/downloads/',
   $counter_file );

The $id is the Freesound.org id for the sample to download, the directory is where to download it and the $counter_file is a location to put the progress info. It’s in this simple format:

10943051:12578220:87

ie 87 percent complete (10943051 bytes of 12578220 total written to disk). Handy for creating a progress bar in CSS and JavaScript (and AJAX).

The $file will be the fully qualified path to the file, and the file is named as it is on Freesound.org with the right extension (.wav, .aif, .mp3 etc).

Snippets for web app visuals

The download.cgi file is separate in my examples because it is called with an AJAX request in the JavaScript of my elemenski.html :

elemenstki.js:

var ajax = new XMLHttpRequest();
ajax.onreadystatechange = function () {
   if (ajax.readyState == 4) {
   }
};
ajax.open("GET", "/cgi-bin/elementski/download.cgi?id=" + freesound_sample_id, true);
ajax.send(null);

The counter_file is read continuously until 100% :

ajax.onreadystatechange = function () {
   if (ajax.readyState == 4) {
      parse_response_from_cgi(ajax.responseText, freesound_sample_id);
   }
};
ajax.open("GET", "/cgi-bin/elementski/ping.cgi?id=" + freesound_sample_id, true);
ajax.send(null);

Here a ping.cgi call is made, which just reads and returns the state of the counter_file until 100%. parse_response_from_cgi just reads the counter_file and converts it into a progress bar, a bit like this :

js function parse_response_from_cgi:

var parts = response.split(":");
var download_container = document.getElementById('download_container_' + freesound_sample_id);
var download_bar       = document.getElementById('download_bar_'       + freesound_sample_id);

var download_width  = parts[2] + "%";

download_container.style.border    = "1px solid green";
download_container.style.width     = "250px";
download_container.style.float     = "right";
download_container.style.marginTop = "5px";

download_bar.style.width           = download_width;
download_bar.style.border          = "1px solid blue";
download_bar.style.height          = "5px";
download_bar.style.background      = "blue";

Kudos to Kirsle for this way of integrating Perl and JavaScript for progress bars.

That’s just a flavour, go to GitHub to download all the goodies and see what can be done (including the JavaScript to do the timeline and logic for play/pause/stop etc).

Here are some screenshots of what it could look like :

one-sample-elementski

Sample showing info and buttons.

playing-timeline

Playing with timeline bar above the sample

downloading

Downloading progress bar.

downloaded-noshadow

Downloaded, no shadow on button.

 

Lots of fun to be had then, with Freesound and its new Perl wrapper!

CheersO

~andy~