File Coverage

blib/lib/WWW/Grooveshark.pm
Criterion Covered Total %
statement 52 227 22.9
branch 7 26 26.9
condition 8 24 33.3
subroutine 13 65 20.0
pod 55 55 100.0
total 135 397 34.0


line stmt bran cond sub pod time code
1             package WWW::Grooveshark;
2              
3 1     1   22227 use 5.006;
  1         5  
  1         42  
4 1     1   5 use strict;
  1         2  
  1         37  
5 1     1   6 use warnings;
  1         2  
  1         78  
6              
7             =head1 NAME
8              
9             WWW::Grooveshark - Perl wrapper for the Grooveshark API
10              
11             =head1 VERSION
12              
13             This document describes C version 0.02 (July 22, 2009).
14              
15             The latest version is hosted on Google Code as part of
16             L. Significant changes are also
17             contributed to CPAN: http://search.cpan.org/dist/WWW-Grooveshark/.
18              
19             =cut
20              
21             our $VERSION = '0.02';
22             $VERSION = eval $VERSION;
23              
24             =head1 SYNOPSIS
25              
26             Basic use is demonstrated here. See L for details.
27              
28             use WWW::Grooveshark;
29              
30             my $gs = WWW::Grooveshark->new(https => 1, agent => "my-nice-robot/0.1");
31              
32             my $r;
33             $r = $gs->session_start(apiKey => $secret) or die $r->fault_line;
34            
35             for($gs->search_songs(query => "The Beatles", limit => 10)->songs) {
36             printf("%s", $_->{songName});
37             printf(" by %s", $_->{artistName});
38             printf(" on %s\n", $_->{albumName});
39             printf(" <%s>\n", $_->{liteUrl});
40             }
41            
42             # session automatically ended by destructor
43              
44             =head1 DESCRIPTION
45              
46             Grooveshark is an internationally-available online music search, streaming,
47             and recommendation service. C wraps this service's API in
48             an object-oriented Perl interface, allowing you to programmatically search
49             for songs, artists, albums, or playlists; browse popular music; get song
50             recommendations; manage playlists; and more.
51              
52             =head1 API KEYS
53              
54             ...are needed to use the Grooveshark API. E-mail
55             Edevelopers@grooveshark.comE to get one. They'll probably also link
56             you to the official API page, which seems to still be in beta.
57              
58             =cut
59              
60 1     1   5 use Carp;
  1         2  
  1         112  
61 1     1   6 use Digest::MD5 qw(md5_hex);
  1         8  
  1         57  
62 1     1   1131 use JSON::Any;
  1         27566  
  1         7  
63 1     1   11059 use URI::Escape;
  1         1754  
  1         96  
64              
65 1     1   722 use WWW::Grooveshark::Response qw(:fault);
  1         3  
  1         6107  
66              
67             our @ISA = ();
68              
69             =head1 CONSTRUCTOR
70              
71             To use this module, you'll have to create a C instance. The
72             default, argumentless constructor should be adequate, but customization is
73             possible through key-value options.
74              
75             =over 4
76              
77             =item WWW::Grooveshark->new( %OPTIONS )
78              
79             Prepares a new C object with the specified options, which are
80             passed in as key-value pairs, as in a hash. Accepted options are:
81              
82             =over 4
83              
84             =item I
85              
86             Whether or not to use HTTPS for API calls. Defaults to false, i.e. just use
87             HTTP.
88              
89             =item I
90              
91             The hostname to use for the Grooveshark API service. Defaults to
92             "api.grooveshark.com" unless C is true, in which case it defaults to
93             "staging.api.grooveshark.com".
94              
95             =item I
96              
97             Path (relative to the hostname) to request for API calls. Defaults to "ws".
98              
99             =item I
100              
101             Version of the Grooveshark API you plan on using. Defaults to 1.0.
102              
103             =item I
104              
105             The query string to include in API call requests. May be blank. Defaults to
106             "json" so that the full default API root URL becomes
107             Ehttp://api.grooveshark.com/ws/1.0/?jsonE.
108              
109             =item I
110              
111             Value to use for the C HTTP header. Defaults to
112             "WWW::Grooveshark/### libwww-perl/###", where the "###" are substituted with
113             the appropriate versions. This is provided for convenience: the user-agent
114             string can also be set in the C (see below). If it's set in
115             both places, this one takes precedence.
116              
117             =item I
118              
119             Name of the L compatible class to be used internally by the
120             newly-created object. Defaults to L.
121              
122             =item I
123              
124             Hashref of arguments to pass to the constructor of the aforementioned
125             C. Defaults to no arguments.
126              
127             =back
128              
129             Options not listed above are ignored.
130              
131             =cut
132              
133             sub new {
134 1     1 1 1000 my($pkg, %opts) = @_;
135              
136             # user-agent constructor args
137 1   50     10 my $ua_args = $opts{useragent_args} || {};
138            
139             # user-agent string
140 1 50       8 $ua_args->{agent} = $opts{agent} if defined $opts{agent};
141 1   33     23 $ua_args->{agent} ||= __PACKAGE__ . "/$VERSION ";
142              
143             # prepare user-agent object
144 1   50     8 my $ua_class = $opts{useragent_class} || 'LWP::UserAgent';
145 1         76 eval "require $ua_class";
146 1 50       80108 croak $@ if $@;
147 1         14 my $ua = $ua_class->new(%$ua_args);
148              
149 1 50       4439 my $default_service = $opts{staging} ?
150             'staging.api.grooveshark.com' :
151             'api.grooveshark.com';
152              
153 1   33     36 return bless({
      50        
      50        
      50        
      50        
154             _ua => $ua,
155             _service => $opts{service} || $default_service,
156             _path => $opts{path} || 'ws',
157             _api_version => $opts{api_version} || '1.0',
158             _query_string => $opts{query_string} || 'json',
159             _https => $opts{https} || 0,
160             _session_id => undef,
161             _json => new JSON::Any,
162             }, $pkg);
163             }
164              
165             =back
166              
167             =head1 DESTRUCTOR
168              
169             I like code that cleans up after itself. If a program starts by creating a
170             session, it's only logical that it should finish by ending it. But should it
171             be up to the programmer to manage when that happens? Anyone can be forgetful.
172              
173             Enter the destructor. Continue explicitly cleaning up, but if you forget to
174             do so, the destructor has your back. When a C object gets
175             garbage collected, it will destroy its session if any.
176              
177             =cut
178              
179             sub DESTROY {
180 1     1   269 my $self = shift;
181 1 50       152 $self->session_destroy if $self->sessionID;
182             }
183              
184             =head1 MANAGEMENT METHODS
185              
186             The following methods do not issue any API calls but deal with management of
187             the C object itself. Ideally, you won't have to touch these
188             methods too often. If you find yourself being insufficiently lazy, let me
189             know how I can make this module smarter.
190              
191             =over 4
192              
193             =item $gs->sessionID( )
194              
195             Returns the Grooveshark API session ID, or C if there is no active
196             session.
197              
198             =back
199              
200             =cut
201              
202             sub sessionID {
203 3     3 1 218 return shift->{_session_id};
204             }
205              
206             =head1 API METHODS
207              
208             The methods listed here directly wrap the methods of Groveshark's JSON-RPC
209             API. As you may have noticed, there is a very complex mapping between the
210             API's official methods and those of this interface: replace the period ('.')
211             with an underscore ('_'). As with the constructor, pass arguments as
212             hash-like key-value pairs, so for example, to get the 11th through 20th most
213             popular songs, I would:
214              
215             my $response = $gs->popular_getSongs(limit => 10, page => 2);
216              
217             All API methods return L objects, even in case of
218             errors, but their boolean evaluation is Led to give false for fault
219             responses. Make a habit of checking that method calls were successful:
220              
221             die $response->fault_line unless $response;
222              
223             Access result elements by using the key as the method name. In list context,
224             dereferencing takes place automagically, saving you a few characters:
225              
226             my @songs = $response->songs;
227              
228             But after this first "layer" you're stuck dealing with hashrefs, as in the
229             L (though perhaps this will change in the future if I'm up to it):
230              
231             for(@songs) {
232             printf("%s", $_->{songName});
233             printf(" by %s", $_->{artistName});
234             printf(" on %s\n", $_->{albumName});
235             printf(" <%s>\n", $_->{liteUrl});
236             }
237              
238             Check the official API documentation for valid keys. Alternatively, experiment!
239              
240             use Data::Dumper;
241             print Dumper($response);
242              
243             This module's interface aims to parallel the official API as much as possible.
244             Consequently, all methods take argument names identical to the official ones.
245             However, some methods are "overloaded." For example,
246             C gives you the option of passing a plaintext
247             C rather than a C, handling C generation for you.
248              
249             Some methods may also have side effects. These are generally "harmless": for
250             example, successful C and C calls store the
251             returned session ID so that it can be passed in the header of subsequent API
252             calls.
253              
254             Alternate method arguments and any side effects are listed where applicable.
255              
256             =head2 ALBUM
257              
258             =over 4
259              
260             =item $gs->album_about( albumID => $ALBUM_ID )
261              
262             Returns meta-information for the album with the specified $ALBUM_ID, such as
263             album name, artist ID, and artist name.
264              
265             =cut
266              
267             sub album_about {
268 0     0 1 0 my($self, %args) = @_;
269 0         0 my $ret = $self->_call('album.about', %args);
270 0         0 return $ret;
271             }
272              
273             =item $gs->album_getSongs( albumID => $ALBUM_ID [, limit => $LIMIT ] [, page => $PAGE ] )
274              
275             Returns all the songs on the album with the specified $ALBUM_ID, as well as
276             song meta-information.
277              
278             =cut
279              
280             sub album_getSongs {
281 0     0 1 0 my($self, %args) = @_;
282 0         0 my $ret = $self->_call('album.getSongs', %args);
283 0         0 return $ret;
284             }
285              
286             =back
287              
288             =head2 ARTIST
289              
290             =over 4
291              
292             =item $gs->artist_about( artistID => $ARTIST_ID )
293              
294             Returns information for the artist with the specified $ARTIST_ID.
295              
296             =cut
297              
298             sub artist_about {
299 0     0 1 0 my($self, %args) = @_;
300 0         0 my $ret = $self->_call('artist.about', %args);
301 0         0 return $ret;
302             }
303              
304             =item $gs->artist_getAlbums( artistID => $ARTIST_ID [, limit => $LIMIT ] [, page => $PAGE ] )
305              
306             Returns the albums of the artist with the specified $ARTIST_ID, as well as
307             album meta-information.
308              
309             =cut
310              
311             sub artist_getAlbums {
312 0     0 1 0 my($self, %args) = @_;
313 0         0 my $ret = $self->_call('artist.getAlbums', %args);
314 0         0 return $ret;
315             }
316              
317             =item $gs->artist_getSimilar( artistID => $ARTIST_ID [, limit => $LIMIT ] [, page => $PAGE ] )
318              
319             Returns a list of artists similar to the one with the specified $ARTIST_ID.
320              
321             =cut
322              
323             sub artist_getSimilar {
324 0     0 1 0 my($self, %args) = @_;
325 0         0 my $ret = $self->_call('artist.getSimilar', %args);
326 0         0 return $ret;
327             }
328              
329             =item $gs->artist_getSongs( artistID => $ARTIST_ID [, limit => $LIMIT ] [, page => $PAGE ] )
330              
331             Returns the songs on the albums of the artist with the specified $ARTIST_ID, as
332             well as song meta-information.
333              
334             =cut
335              
336             sub artist_getSongs {
337 0     0 1 0 my($self, %args) = @_;
338 0         0 my $ret = $self->_call('artist.getSongs', %args);
339 0         0 return $ret;
340             }
341              
342             =item $gs->artist_getTopRatedSongs( artistID => $ARTIST_ID [, limit => $LIMIT ] [, page => $PAGE ] )
343              
344             Returns the top rated songs of the artist with the specified $ARTIST_ID, as
345             well as song meta-information. Use at your own risk: the existence of this
346             method was not mentioned in the official API documentation at the time of
347             this writing; it was discovered through the sandbox tool.
348              
349             =cut
350              
351             sub artist_getTopRatedSongs {
352 0     0 1 0 my($self, %args) = @_;
353 0         0 my $ret = $self->_call('artist.getTopRatedSongs', %args);
354 0         0 return $ret;
355             }
356              
357             =back
358              
359             =head2 AUTOPLAY
360              
361             =over 4
362              
363             =item $gs->autoplay_frown( autoplaySongID => $AUTOPLAY_SONG_ID )
364              
365             "Frowns" the song with the specified $AUTOPLAY_SONG_ID in the current Autoplay
366             session, indicating that the song is not liked and making the Autoplay session
367             suggest fewer songs like it.
368              
369             =cut
370              
371             sub autoplay_frown {
372 0     0 1 0 my($self, %args) = @_;
373 0         0 my $ret = $self->_call('autoplay.frown', %args);
374 0         0 return $ret;
375             }
376              
377             =item $gs->autoplay_getNextSong( )
378              
379             Returns the next suggested song in the current Autoplay session, based on the
380             seed songs and any "smiles" or "frowns."
381              
382             =cut
383              
384             sub autoplay_getNextSong {
385 0     0 1 0 my($self, %args) = @_;
386 0         0 my $ret = $self->_call('autoplay.getNextSong', %args);
387 0         0 return $ret;
388             }
389              
390             =item $gs->autoplay_smile( autoplaySongID => $AUTOPLAY_SONG_ID )
391              
392             "Smiles" the song with the specified $AUTOPLAY_SONG_ID in the current Autoplay
393             session, indicating that the song is liked and making the Autoplay session
394             suggest more songs like it.
395              
396             =cut
397              
398             sub autoplay_smile {
399 0     0 1 0 my($self, %args) = @_;
400 0         0 my $ret = $self->_call('autoplay.smile', %args);
401 0         0 return $ret;
402             }
403              
404             =item $gs->autoplay_start( songIDs => \@SONG_IDS )
405              
406             Starts an Autoplay session seeded with the specified song IDs and returns the
407             first song suggestion.
408              
409             =cut
410              
411             sub autoplay_start {
412 0     0 1 0 my($self, %args) = @_;
413 0         0 my $ret = $self->_call('autoplay.start', %args);
414 0         0 return $ret;
415             }
416              
417             =item $gs->autoplay_stop( )
418              
419             Ends the active Autoplay session.
420              
421             =cut
422              
423             sub autoplay_stop {
424 0     0 1 0 my($self, %args) = @_;
425 0         0 my $ret = $self->_call('autoplay.stop', %args);
426 0         0 return $ret;
427             }
428              
429             =back
430              
431             =head2 PLAYLIST
432              
433             =over 4
434              
435             =item $gs->playlist_about( playlistID => $PLAYLIST_ID )
436              
437             Returns information for the playlist with the specified $PLAYLIST_ID, such as
438             its name, description, song count, creation date, etc.
439              
440             =cut
441              
442             sub playlist_about {
443 0     0 1 0 my($self, %args) = @_;
444 0         0 my $ret = $self->_call('playlist.about', %args);
445 0         0 return $ret;
446             }
447              
448             =item $gs->playlist_addSong( playlistID => $PLAYLIST_ID , songID => $SONG_ID [, position => $POSITION ] )
449              
450             Adds the song with the specified $SONG_ID to the playlist with the specified
451             $PLAYLIST_ID at $POSITION (or at the end, if $POSITION is omitted). Valid
452             positions start from 1: a value of zero is equivalent to not specifying any.
453             To succeed, this method requires being authenticated as the playlist's
454             creator.
455              
456             =cut
457              
458             sub playlist_addSong {
459 0     0 1 0 my($self, %args) = @_;
460 0         0 my $ret = $self->_call('playlist.addSong', %args);
461 0         0 return $ret;
462             }
463              
464             =item $gs->playlist_create( name => $NAME, about => $DESCRIPTION )
465              
466             Creates a playlist with the specified $NAME and $DESCRIPTION and returns the
467             playlist ID. Requires user authentication.
468              
469             =cut
470              
471             sub playlist_create {
472 0     0 1 0 my($self, %args) = @_;
473 0         0 my $ret = $self->_call('playlist.create', %args);
474 0         0 return $ret;
475             }
476              
477             =item $gs->playlist_delete( playlistID => $PLAYLIST_ID )
478              
479             Deletes the playlist with the specified $PLAYLIST_ID. Requires being
480             authenticated as the playlist's creator.
481              
482             =cut
483              
484             sub playlist_delete {
485 0     0 1 0 my($self, %args) = @_;
486 0         0 my $ret = $self->_call('playlist.delete', %args);
487 0         0 return $ret;
488             }
489              
490             =item $gs->playlist_getSongs( playlistID => $PLAYLIST_ID [, limit => $LIMIT ] [, page => $PAGE ] )
491              
492             Returns the songs on the playlist with the specified $PLAYLIST_ID, as well as
493             song meta-information.
494              
495             =cut
496              
497             sub playlist_getSongs {
498 0     0 1 0 my($self, %args) = @_;
499 0         0 my $ret = $self->_call('playlist.getSongs', %args);
500 0         0 return $ret;
501             }
502              
503             =item $gs->playlist_moveSong( playlistID => $PLAYLIST_ID , position => $POSITION , newPosition => $NEW_POSITION )
504              
505             Moves the song at $POSITION in the playlist with the specified $PLAYLIST_ID to
506             $NEW_POSITION. Valid positions start from 1. A $NEW_POSITION of zero moves
507             the song to the end of the playlist. To succeed, this method requires being
508             authenticated as the playlist's creator.
509              
510             =cut
511              
512             sub playlist_moveSong {
513 0     0 1 0 my($self, %args) = @_;
514 0         0 my $ret = $self->_call('playlist.moveSong', %args);
515 0         0 return $ret;
516             }
517              
518             =item $gs->playlist_removeSong( playlistID => $PLAYLIST_ID , position => $POSITION )
519              
520             Removes the song at $POSITION from the playlist with the specified
521             $PLAYLIST_ID. Valid positions start from 1. To succeed, this method requires
522             being authenticated as the playlist's creator.
523              
524             =cut
525              
526             sub playlist_removeSong {
527 0     0 1 0 my($self, %args) = @_;
528 0         0 my $ret = $self->_call('playlist.removeSong', %args);
529 0         0 return $ret;
530             }
531              
532             =item $gs->playlist_rename( playlistID => $PLAYLIST_ID , name => $NAME )
533              
534             Renames the playlist with the specified $PLAYLIST_ID to $NAME. Requires being
535             authenticated as the playlist's creator.
536              
537             =cut
538              
539             sub playlist_rename {
540 0     0 1 0 my($self, %args) = @_;
541 0         0 my $ret = $self->_call('playlist.rename', %args);
542 0         0 return $ret;
543             }
544              
545             =item $gs->playlist_replace( playlistID => $PLAYLIST_ID , songIDs = \@SONG_IDS )
546              
547             Replaces the contents of the playlist with the specified $PLAYLIST_ID with the
548             songs corresponding to the given @SONG_IDS, in the specified order. To succeed,
549             this method requires being authenticated as the playlist's creator. (But at
550             the time of this writing, this didn't seem to work as expected, instead
551             returning an internal server error message.)
552              
553             =cut
554              
555             sub playlist_replace {
556 0     0 1 0 my($self, %args) = @_;
557 0         0 my $ret = $self->_call('playlist.replace', %args);
558 0         0 return $ret;
559             }
560              
561             =back
562              
563             =head2 POPULAR
564              
565             =over 4
566              
567             =item $gs->popular_getAlbums( [ limit => $LIMIT ] [, page => $PAGE ] )
568              
569             Gets a list of popular albums (and meta-information) from Grooveshark's
570             billboard.
571              
572             =cut
573              
574             sub popular_getAlbums {
575 0     0 1 0 my($self, %args) = @_;
576 0         0 my $ret = $self->_call('popular.getAlbums', %args);
577 0         0 return $ret;
578             }
579              
580             =item $gs->popular_getArtists( [ limit => $LIMIT ] [, page => $PAGE ] )
581              
582             Gets a list of popular artists from Grooveshark's billboard.
583              
584             =cut
585              
586             sub popular_getArtists {
587 0     0 1 0 my($self, %args) = @_;
588 0         0 my $ret = $self->_call('popular.getArtists', %args);
589 0         0 return $ret;
590             }
591              
592             =item $gs->popular_getSongs( [ limit => $LIMIT ] [, page => $PAGE ] )
593              
594             Gets a list of popular songs (and meta-information) from Grooveshark's
595             billboard.
596              
597             =cut
598              
599             sub popular_getSongs {
600 0     0 1 0 my($self, %args) = @_;
601 0         0 my $ret = $self->_call('popular.getSongs', %args);
602 0         0 return $ret;
603             }
604              
605             =back
606              
607             =head2 SEARCH
608              
609             =over 4
610              
611             =item $gs->search_albums( query => $QUERY [, limit => $LIMIT ] [, page => $PAGE ] )
612              
613             Searches for albums with names that match $QUERY.
614              
615             =cut
616              
617             sub search_albums {
618 0     0 1 0 my($self, %args) = @_;
619 0         0 my $ret = $self->_call('search.albums', %args);
620 0         0 return $ret;
621             }
622              
623             =item $gs->search_artists( query => $QUERY [, limit => $LIMIT ] [, page => $PAGE ] )
624              
625             Searches for artists with names that match $QUERY.
626              
627             =cut
628              
629             sub search_artists {
630 0     0 1 0 my($self, %args) = @_;
631 0         0 my $ret = $self->_call('search.artists', %args);
632 0         0 return $ret;
633             }
634              
635             =item $gs->search_playlists( query => $QUERY [, limit => $LIMIT ] [, page => $PAGE ] )
636              
637             Searches for playlists that match $QUERY by name or by meta-information
638             of composing songs.
639              
640             =cut
641              
642             sub search_playlists {
643 0     0 1 0 my($self, %args) = @_;
644 0         0 my $ret = $self->_call('search.playlists', %args);
645 0         0 return $ret;
646             }
647              
648             =item $gs->search_songs( query => $QUERY [, limit => $LIMIT ] [, page => $PAGE ] )
649              
650             Searches for songs that match $QUERY by name or meta-information.
651              
652             =cut
653              
654             sub search_songs {
655 0     0 1 0 my($self, %args) = @_;
656 0         0 my $ret = $self->_call('search.songs', %args);
657 0         0 return $ret;
658             }
659              
660             =back
661              
662             =head2 SERVICE
663              
664             =over 4
665              
666             =item $gs->service_getMethods( )
667              
668             Gets a list of the methods supported by the service, as well as the names of
669             their parameters. Calling this method doesn't require a session.
670              
671             =cut
672              
673             sub service_getMethods {
674 1     1 1 10032 my($self, %args) = @_;
675 1         7 my $ret = $self->_call('service.getMethods', %args);
676 1         17 return $ret;
677             }
678              
679             =item $gs->service_getVersion( )
680              
681             Gets the version of the API supported by the service. Calling this method
682             doesn't require a session.
683              
684             =cut
685              
686             sub service_getVersion {
687 0     0 1 0 my($self, %args) = @_;
688 0         0 my $ret = $self->_call('service.getVersion', %args);
689 0         0 return $ret;
690             }
691              
692             =item $gs->service_ping( )
693              
694             Checks that the service is alive. Calling this method doesn't require a
695             session. Useful for testing (and for getting a "Hello, world" greeting in
696             some language).
697              
698             =cut
699              
700             sub service_ping {
701 0     0 1 0 my($self, %args) = @_;
702 0         0 my $ret = $self->_call('service.ping', %args);
703 0         0 return $ret;
704             }
705              
706             =back
707              
708             =head2 SESSION
709              
710             =over 4
711              
712             =item $gs->session_createUserAuthToken( username => $USERNAME , pass => $PASS | hashpass => $HASHPASS )
713              
714             Creates an authentication token for the specified $USERNAME. Authentication
715             requires a $HASHPASS, which is a hexadecimal MD5 hash of the concatenation of
716             $USERNAME and a hexadecimal MD5 hash of $PASS. If you're storing the password
717             as plaintext, don't bother generating the $HASHPASS yourself: just omit the
718             $HASHPASS and give C $PASS> to this method. If you specify both a
719             $HASHPASS and a $PASS, the $HASHPASS will take precedence (but don't try it).
720             Regardless, the $PASS will be removed from the arguments that are passed during
721             the API call.
722              
723             =cut
724              
725             sub session_createUserAuthToken {
726 0     0 1 0 my($self, %args) = @_;
727            
728             # make hashpass, unless it already exists
729 0 0       0 if(exists($args{hashpass})) {
730 0         0 delete $args{pass};
731             }
732             else {
733 0 0 0     0 if(exists($args{username}) && exists($args{pass})) {
734 0         0 $args{hashpass} = md5_hex($args{username}, md5_hex($args{pass}));
735             }
736             else {
737 0         0 carp 'Need username and pass to create authentication token';
738             }
739 0         0 delete $args{pass};
740             }
741            
742 0         0 my $ret = $self->_call('session.createUserAuthToken', %args);
743 0         0 return $ret;
744             }
745              
746             =item $gs->session_destroy( )
747              
748             Destroys the currently active session. As a side effect, removes the stored
749             session ID so that subsequent C calls on this C
750             object will return C.
751              
752             =cut
753              
754             sub session_destroy {
755 0     0 1 0 my($self, %args) = @_;
756 0         0 my $ret = $self->_call('session.destroy', %args);
757            
758             # kill the stored session ID if destroying was successful
759 0 0       0 $self->{_session_id} = undef if $ret;
760            
761 0         0 return $ret;
762             }
763              
764             =item $gs->session_destroyAuthToken( token => $TOKEN )
765              
766             Destroys an auth token so that subsequent attempts to use it to login will
767             fail.
768              
769             =cut
770              
771             sub session_destroyAuthToken {
772 0     0 1 0 my($self, %args) = @_;
773 0         0 my $ret = $self->_call('session.destroyAuthToken', %args);
774 0         0 return $ret;
775             }
776              
777             =item $gs->session_get( )
778              
779             Gets the session ID of the currently active session. Presumably this updates
780             every once in a while because there wouldn't be much use in this method
781             otherwise: an active session is required to call it, and returning the same
782             session ID would be a waste of an API call... Assuming this does update,
783             calling this method has the side effect of updating the session ID of this
784             C object.
785              
786             =cut
787              
788             sub session_get {
789 0     0 1 0 my($self, %args) = @_;
790 0         0 my $ret = $self->_call('session.get', %args);
791            
792             # save the session ID given in the response
793 0 0       0 $self->{_session_id} = $ret->sessionID if $ret;
794            
795 0         0 return $ret;
796             }
797              
798             =item $gs->session_getUserID( )
799              
800             Gets the user ID of the currently logged-in user.
801              
802             =cut
803              
804             sub session_getUserID {
805 0     0 1 0 my($self, %args) = @_;
806 0         0 my $ret = $self->_call('session.getUserID', %args);
807 0         0 return $ret;
808             }
809              
810             =item $gs->session_loginViaAuthToken( token => $TOKEN )
811              
812             Logs in using a $TOKEN created using C.
813              
814             =cut
815              
816             sub session_loginViaAuthToken {
817 0     0 1 0 my($self, %args) = @_;
818 0         0 my $ret = $self->_call('session.loginViaAuthToken', %args);
819 0         0 return $ret;
820             }
821              
822             =item $gs->session_logout( )
823              
824             Logs out the logged-in user.
825              
826             =cut
827              
828             sub session_logout {
829 0     0 1 0 my($self, %args) = @_;
830 0         0 my $ret = $self->_call('session.logout', %args);
831 0         0 return $ret;
832             }
833              
834             =item $gs->session_start( apiKey => $API_KEY [, mobileID => $MOBILE_ID ] )
835              
836             Starts a session using the specified $API_KEY. This method must be called
837             before using (nearly) all of the other methods. The returned session ID will
838             be stored in this C object, accessible via calls to
839             C, and automatically placed in the header of subsequent API calls.
840             $MOBILE_ID isn't mentioned in the official documentation and appears only in
841             the sandbox tool.
842              
843             =cut
844              
845             sub session_start {
846 0     0 1 0 my($self, %args) = @_;
847            
848             # remove a prior session ID, but store this value
849 0         0 my $old_session_id = $self->{_session_id};
850 0         0 $self->{_session_id} = undef;
851            
852 0         0 my $ret = $self->_call('session.start', %args);
853            
854 0 0       0 if($ret) {
855             # save the session ID given in the response
856 0         0 $self->{_session_id} = $ret->sessionID;
857             }
858             else {
859             # restore old session ID
860 0         0 $self->{_session_id} = $old_session_id;
861             }
862            
863 0         0 return $ret;
864             }
865              
866             =back
867              
868             =head2 SONG
869              
870             =over 4
871              
872             =item $gs->song_about( songID => $SONG_ID )
873              
874             Returns meta-information for the song with the specified $SONG_ID, such as
875             song name, album name, album ID, artist name, artist ID, etc.
876              
877             =cut
878              
879             sub song_about {
880 0     0 1 0 my($self, %args) = @_;
881 0         0 my $ret = $self->_call('song.about', %args);
882 0         0 return $ret;
883             }
884              
885             =item $gs->song_favorite( songID => $SONG_ID )
886              
887             Marks the song with the specified $SONG_ID as a favorite. Requires user
888             authentication.
889              
890             =cut
891              
892             sub song_favorite {
893 0     0 1 0 my($self, %args) = @_;
894 0         0 my $ret = $self->_call('song.favorite', %args);
895 0         0 return $ret;
896             }
897              
898             =item $gs->song_getSimilar( songID => $SONG_ID [, limit => $LIMIT ] [, page => $PAGE ] )
899              
900             Gets a list of songs similar to the one with the specified $SONG_ID, as well as
901             their meta-information.
902              
903             =cut
904              
905             sub song_getSimilar {
906 0     0 1 0 my($self, %args) = @_;
907 0         0 my $ret = $self->_call('song.getSimilar', %args);
908 0         0 return $ret;
909             }
910              
911             =item $gs->song_getStreamKey( songID => $SONG_ID )
912              
913             Gets a streamKey for the song with the specified $SONG_ID (needed to authorize
914             playback for some Grooveshark embeddable players).
915              
916             =cut
917              
918             sub song_getStreamKey {
919 0     0 1 0 my($self, %args) = @_;
920 0         0 my $ret = $self->_call('song.getStreamKey', %args);
921 0         0 return $ret;
922             }
923              
924             =item $gs->song_getStreamUrl( songID => $SONG_ID )
925              
926             Gets an URL for streaming playback of the song with the specified $SONG_ID.
927             According to the response header, this method is deprecated and
928             C should be used instead.
929              
930             =cut
931              
932             sub song_getStreamUrl {
933 0     0 1 0 my($self, %args) = @_;
934 0         0 my $ret = $self->_call('song.getStreamUrl', %args);
935 0         0 return $ret;
936             }
937              
938             =item $gs->song_getStreamUrlEx( songID => $SONG_ID [, lowBitrate => $LOW_BITRATE ] )
939              
940             The supposedly preferred alternative to C. Use at your
941             own risk: the existence of this method was not mentioned in the official API
942             documentation at the time of this writing; it was discovered through the
943             sandbox tool as well as the deprecation message in the header of
944             C responses.
945              
946             =cut
947              
948             sub song_getStreamUrlEx {
949 0     0 1 0 my($self, %args) = @_;
950 0         0 my $ret = $self->_call('song.getStreamUrlEx', %args);
951 0         0 return $ret;
952             }
953              
954             =item $gs->song_getWidgetEmbedCode( songID => $SONG_ID [, theme => $THEME ] [, pxHeight => $HEIGHT ] [, pxWidth => $WIDTH ] [, ap => $AP ] )
955              
956             Gets HTML code for embedding the song with the specified $SONG_ID. The code
957             may be customized by specifying a pixel $HEIGHT and $WIDTH as well as a theme
958             for the widget, which must be in C. The $AP is
959             optional and appears only in the sandbox tool and not the official
960             documentation: its meaning is unknown.
961              
962             =cut
963              
964             sub song_getWidgetEmbedCode {
965 0     0 1 0 my($self, %args) = @_;
966 0         0 my $ret = $self->_call('song.getWidgetEmbedCode', %args);
967 0         0 return $ret;
968             }
969              
970             =item $gs->song_getWidgetEmbedCodeFbml( songID => $SONG_ID [, theme => $THEME ] [, pxHeight => $HEIGHT ] [, pxWidth => $WIDTH ] [, ap => $AP ]
971              
972             This is in fact not an API method but a wrapper for C
973             that modifies the returned HTML code to FBML so it can be used in Facebook
974             applications. This method is experimental: use it at your own risk.
975              
976             =cut
977              
978             sub song_getWidgetEmbedCodeFbml {
979 0     0 1 0 my $ret = shift->song_getWidgetEmbedCode(@_);
980              
981 0 0       0 if($ret) {
982 0         0 my $code = $ret->{result}->{embed};
983 0         0 $code =~ /\s*<\/embed>/;
984 0         0 $code = "";
985 0         0 $ret->{result}->{embed} = $code;
986             }
987              
988 0         0 return $ret;
989             }
990              
991             =item $gs->song_unfavorite( songID => $SONG_ID )
992              
993             Removes the song with the specified $SONG_ID from the logged-in user's list
994             of favorites.
995              
996             =cut
997              
998             sub song_unfavorite {
999 0     0 1 0 my($self, %args) = @_;
1000 0         0 my $ret = $self->_call('song.unfavorite', %args);
1001 0         0 return $ret;
1002             }
1003              
1004             =back
1005              
1006             =head2 TINYSONG
1007              
1008             =over 4
1009              
1010             =item $gs->tinysong_create( songID => $SONG_ID | ( query => $QUERY [, useFirstResult => $USE_FIRST_RESULT ] ) )
1011              
1012             Creates a tiny URL that links to the song with the specified $SONG_ID. The
1013             method seems to also allow searching (if a $QUERY and whether to
1014             $USE_FIRST_RESULT are specified), but this form appears to be buggy at the
1015             time of this writing, and is discouraged.
1016              
1017             =cut
1018              
1019             sub tinysong_create {
1020 0     0 1 0 my($self, %args) = @_;
1021 0         0 my $ret = $self->_call('tinysong.create', %args);
1022 0         0 return $ret;
1023             }
1024              
1025             =item $gs->tinysong_getExpandedUrl( tinySongUrl => $TINYSONG_URL )
1026              
1027             Expands a TinySong URL into the full URL to which it redirects.
1028              
1029             =cut
1030              
1031             sub tinysong_getExpandedUrl {
1032 0     0 1 0 my($self, %args) = @_;
1033 0         0 my $ret = $self->_call('tinysong.getExpandedUrl', %args);
1034 0         0 return $ret;
1035             }
1036              
1037             =back
1038              
1039             =head2 USER
1040              
1041             =over 4
1042              
1043             =item $gs->user_about( $user_id => $USER_ID )
1044              
1045             Returns information about the user with the specified $USER_ID, such as
1046             username, date joined, etc.
1047              
1048             =cut
1049              
1050             sub user_about {
1051 0     0 1 0 my($self, %args) = @_;
1052 0         0 my $ret = $self->_call('user.about', %args);
1053 0         0 return $ret;
1054             }
1055              
1056             =item $gs->user_getFavoriteSongs( $user_id => $USER_ID [, limit => $LIMIT ] [, page => $PAGE ] )
1057              
1058             Returns songs (and meta-information) from the favorite list of the user with
1059             the specified $USER_ID.
1060              
1061             =cut
1062              
1063             sub user_getFavoriteSongs {
1064 0     0 1 0 my($self, %args) = @_;
1065 0         0 my $ret = $self->_call('user.getFavoriteSongs', %args);
1066 0         0 return $ret;
1067             }
1068              
1069             =item $gs->user_getPlaylists( $user_id => $USER_ID [, limit => $LIMIT ] [, page => $PAGE ] )
1070              
1071             Gets the playlists created by the user with the specified $USER_ID.
1072              
1073             =cut
1074              
1075             sub user_getPlaylists {
1076 0     0 1 0 my($self, %args) = @_;
1077 0         0 my $ret = $self->_call('user.getPlaylists', %args);
1078 0         0 return $ret;
1079             }
1080              
1081             =back
1082              
1083             =cut
1084              
1085             ################################################################################
1086              
1087             sub _call {
1088 1     1   4 my($self, $method, %param) = @_;
1089              
1090             # print STDERR "Called $method\n";
1091              
1092 1         6 my $req = {
1093             header => {sessionID => $self->sessionID},
1094             method => $method,
1095             parameters => \%param,
1096             };
1097              
1098             # use Data::Dumper; print STDERR Dumper($req);
1099              
1100 1         10 my $json = $self->{_json}->encode($req);
1101 1 50       61 my $url = sprintf('%s://%s/%s/%s/', ($self->{_https} ? 'https' : 'http'),
1102             map($self->{$_}, qw(_service _path _api_version)));
1103 1 50       6 if(my $q = $self->{_query_string}) {
1104 1         144 $q = uri_escape($q);
1105 1         37 $url .= '?' . $q;
1106             }
1107 1         9 my $response = $self->{_ua}->post($url,
1108             'Content-Type' => 'text/json',
1109             'Content' => $json,
1110             );
1111              
1112 1         470226 my $ret;
1113 1 50       9 if($response->is_success) {
1114 0   0     0 my $content = $response->decoded_content || $response->content;
1115 0         0 $ret = $self->{_json}->decode($content);
1116             }
1117             else {
1118 1         20 $ret = {
1119             header => {sessionID => $self->sessionID},
1120             fault => {
1121             code => INTERNAL_FAULT,
1122             message => $response->status_line,
1123             },
1124             };
1125             }
1126              
1127             # use Data::Dumper; print STDERR Dumper($ret);
1128              
1129 1         27 return WWW::Grooveshark::Response->new($ret);
1130             }
1131              
1132             1;
1133              
1134             __END__