shortwave 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +10 -0
- data/.gitmodules +3 -0
- data/LICENSE +20 -0
- data/README.rdoc +74 -0
- data/Rakefile +133 -0
- data/VERSION +1 -0
- data/lib/shortwave.rb +12 -0
- data/lib/shortwave/authentication.rb +107 -0
- data/lib/shortwave/facade.rb +3 -0
- data/lib/shortwave/facade/build/facade_builder.rb +199 -0
- data/lib/shortwave/facade/build/facade_template.erb +17 -0
- data/lib/shortwave/facade/lastfm.rb +878 -0
- data/lib/shortwave/facade/remote.rb +86 -0
- data/lib/shortwave/model/album.rb +33 -0
- data/lib/shortwave/model/artist.rb +62 -0
- data/lib/shortwave/model/base_model.rb +112 -0
- data/lib/shortwave/model/chart_dates.rb +15 -0
- data/lib/shortwave/model/comparison.rb +15 -0
- data/lib/shortwave/model/event.rb +55 -0
- data/lib/shortwave/model/group.rb +26 -0
- data/lib/shortwave/model/location.rb +45 -0
- data/lib/shortwave/model/playlist.rb +36 -0
- data/lib/shortwave/model/shout.rb +21 -0
- data/lib/shortwave/model/tag.rb +47 -0
- data/lib/shortwave/model/track.rb +71 -0
- data/lib/shortwave/model/user.rb +70 -0
- data/lib/shortwave/model/venue.rb +37 -0
- data/lib/shortwave/model/weekly_charts.rb +31 -0
- data/lib/shortwave/providers.rb +161 -0
- data/shortwave.gemspec +178 -0
- data/test/authentication_test.rb +64 -0
- data/test/build/build_test.rb +25 -0
- data/test/build/data/intro.yml +2 -0
- data/test/build/data/screens/album_addTags.html +1238 -0
- data/test/build/data/screens/intro_truncated.html +426 -0
- data/test/build/data/screens/tasteometer_compare.html +1274 -0
- data/test/build/data/screens/user_getLovedTracks.html +1278 -0
- data/test/build/data/screens/venue_search.html +1261 -0
- data/test/build/facade_builder_test.rb +23 -0
- data/test/build/parameter_test.rb +43 -0
- data/test/build/remote_method_test.rb +47 -0
- data/test/build/ruby_class_test.rb +12 -0
- data/test/build/ruby_method_test.rb +137 -0
- data/test/helper.rb +35 -0
- data/test/model/album_test.rb +62 -0
- data/test/model/artist_test.rb +103 -0
- data/test/model/chart_dates_test.rb +11 -0
- data/test/model/comparison_test.rb +18 -0
- data/test/model/data/album_info.xml +38 -0
- data/test/model/data/album_search.xml +210 -0
- data/test/model/data/artist_info.xml +58 -0
- data/test/model/data/artist_search.xml +109 -0
- data/test/model/data/artist_shouts.xml +546 -0
- data/test/model/data/artist_top_fans.xml +405 -0
- data/test/model/data/event_info.xml +47 -0
- data/test/model/data/group_members.xml +242 -0
- data/test/model/data/group_weekly_album_chart.xml +1754 -0
- data/test/model/data/group_weekly_artist_chart.xml +604 -0
- data/test/model/data/group_weekly_track_chart.xml +1005 -0
- data/test/model/data/location_events.xml +383 -0
- data/test/model/data/ok.xml +2 -0
- data/test/model/data/playlist_fetch.xml +227 -0
- data/test/model/data/tag_search.xml +110 -0
- data/test/model/data/tag_similar.xml +254 -0
- data/test/model/data/tag_top_albums.xml +805 -0
- data/test/model/data/tag_top_artists.xml +605 -0
- data/test/model/data/tag_top_tags.xml +1254 -0
- data/test/model/data/tag_top_tracks.xml +843 -0
- data/test/model/data/tag_weekly_chart_list.xml +57 -0
- data/test/model/data/tasteometer_compare.xml +54 -0
- data/test/model/data/track_info.xml +53 -0
- data/test/model/data/track_search.xml +195 -0
- data/test/model/data/user_chartlist.xml +90 -0
- data/test/model/data/user_info.xml +16 -0
- data/test/model/data/user_neighbours.xml +484 -0
- data/test/model/data/user_playlists.xml +17 -0
- data/test/model/data/user_recent_tracks.xml +124 -0
- data/test/model/data/user_recommended_artists.xml +454 -0
- data/test/model/data/user_shouts.xml +9 -0
- data/test/model/data/user_weekly_artist_chart.xml +478 -0
- data/test/model/data/venue_events.xml +556 -0
- data/test/model/data/venue_past_events.xml +1778 -0
- data/test/model/data/venue_search.xml +355 -0
- data/test/model/event_test.rb +63 -0
- data/test/model/group_test.rb +45 -0
- data/test/model/location_test.rb +25 -0
- data/test/model/playlist_test.rb +51 -0
- data/test/model/shout_test.rb +23 -0
- data/test/model/tag_test.rb +39 -0
- data/test/model/track_test.rb +67 -0
- data/test/model/user_test.rb +125 -0
- data/test/model/venue_test.rb +60 -0
- data/test/provider/album_provider_test.rb +26 -0
- data/test/provider/artist_provider_test.rb +25 -0
- data/test/provider/group_provider_test.rb +9 -0
- data/test/provider/location_provider_test.rb +9 -0
- data/test/provider/playlist_provider_test.rb +12 -0
- data/test/provider/tag_provider_test.rb +24 -0
- data/test/provider/track_provider_test.rb +26 -0
- data/test/provider/user_provider_test.rb +11 -0
- data/test/provider/venue_provider_test.rb +15 -0
- data/test/provider_test_helper.rb +27 -0
- data/test/remote_test.rb +26 -0
- metadata +209 -0
@@ -0,0 +1,36 @@
|
|
1
|
+
module Shortwave
|
2
|
+
module Model
|
3
|
+
# A playlist
|
4
|
+
#
|
5
|
+
# === Attributes
|
6
|
+
#
|
7
|
+
# +id+:: id
|
8
|
+
# +title+:: title
|
9
|
+
# +description+:: description
|
10
|
+
# +created_at+:: Creation time
|
11
|
+
# +size+:: Number of tracks in the playlist
|
12
|
+
# +duration+:: length in milliseconds of the playlist
|
13
|
+
# +creator_url+:: Last.fm url of creator
|
14
|
+
# +url+:: Last.fm url of playlist
|
15
|
+
class Playlist < BaseModel
|
16
|
+
element :id, Integer
|
17
|
+
element :title, String
|
18
|
+
element :description, String
|
19
|
+
element :created_at, Time, :tag => "date"
|
20
|
+
element :size, Integer
|
21
|
+
element :duration, Integer
|
22
|
+
element :streamable, Boolean
|
23
|
+
element :creator_url, String, :tag => "creator"
|
24
|
+
element :url, String
|
25
|
+
|
26
|
+
def streamable?
|
27
|
+
@streamable
|
28
|
+
end
|
29
|
+
|
30
|
+
# This can only be called on a user's own playlist
|
31
|
+
def add_track(track)
|
32
|
+
@session.playlist_facade.add_track(id, track.name, track.artist_name)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Shortwave
|
2
|
+
module Model
|
3
|
+
# A shout message on Last.fm
|
4
|
+
#
|
5
|
+
# === Attributes
|
6
|
+
#
|
7
|
+
# +message+:: The message text
|
8
|
+
# +time+:: The time the message was written
|
9
|
+
class Shout < BaseModel
|
10
|
+
tag "shout"
|
11
|
+
element :message, String, :tag => "body"
|
12
|
+
element :time, Time, :tag => "date"
|
13
|
+
element :author, String
|
14
|
+
|
15
|
+
# The user who left the shout message
|
16
|
+
def user
|
17
|
+
@user ||= @session.user.build(:name => author)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Shortwave
|
2
|
+
module Model
|
3
|
+
# A Last.fm tag
|
4
|
+
#
|
5
|
+
# ===Attributes
|
6
|
+
#
|
7
|
+
# +name+:: Tag text
|
8
|
+
# +count+:: Number of times this tag has been applied
|
9
|
+
# +url+:: URL on Last.fm site
|
10
|
+
class Tag < BaseModel
|
11
|
+
element :name, String
|
12
|
+
element :count, Integer
|
13
|
+
element :url, String
|
14
|
+
element :streamable, Boolean
|
15
|
+
|
16
|
+
# Can music be streamed from this tag?
|
17
|
+
def streamable?
|
18
|
+
streamable
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns similar tags to this one.
|
22
|
+
def similar
|
23
|
+
link :similar, :Tag, name
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns the most popular albums tagged with this tag.
|
27
|
+
def albums
|
28
|
+
link :top_albums, :Album, name
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns the most popular artists tagged with this tag.
|
32
|
+
def artists
|
33
|
+
link :top_artists, :Artist, name
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns the most popular tracks tagged with this tag.
|
37
|
+
def tracks
|
38
|
+
link :top_tracks, :Track, name
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns the tag text
|
42
|
+
def to_s
|
43
|
+
name
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Shortwave
|
2
|
+
module Model
|
3
|
+
# A track. Tracks are taggable and sharable.
|
4
|
+
#
|
5
|
+
# === Attributes
|
6
|
+
#
|
7
|
+
# +name+:: Track name
|
8
|
+
# +id+:: Last.fm id
|
9
|
+
# +mbid+:: Musicbrainz ID
|
10
|
+
# +listeners+:: number of listeners
|
11
|
+
# +play_count+:: number of times played
|
12
|
+
# +duration+:: Track duration in milliseconds
|
13
|
+
# +url+:: Track url on Last.fm
|
14
|
+
# +artist+:: Track's artist
|
15
|
+
# +album+:: Album track appears on.
|
16
|
+
class Track < BaseModel
|
17
|
+
element :name, String
|
18
|
+
element :id, Integer
|
19
|
+
element :listeners, Integer
|
20
|
+
element :play_count, Integer, :tag => "playcount"
|
21
|
+
element :mbid, String
|
22
|
+
element :duration, Integer
|
23
|
+
element :url, String
|
24
|
+
element :streamable, Boolean
|
25
|
+
has_one :artist, Artist
|
26
|
+
has_one :album, Album
|
27
|
+
|
28
|
+
identified_by "artist.name", :name
|
29
|
+
taggable
|
30
|
+
sharable
|
31
|
+
|
32
|
+
# The most popular tags applied to this track
|
33
|
+
def tags
|
34
|
+
link :top_tags, :Tag, identifiers
|
35
|
+
end
|
36
|
+
|
37
|
+
# This track's fans
|
38
|
+
def fans
|
39
|
+
link :top_fans, :User, identifiers
|
40
|
+
end
|
41
|
+
|
42
|
+
# Similar tracks to this one.
|
43
|
+
def similar
|
44
|
+
link :similar, :Track, identifiers
|
45
|
+
end
|
46
|
+
|
47
|
+
# Love this track
|
48
|
+
def love
|
49
|
+
@session.track_facade.ban(name, artist.name)
|
50
|
+
# TODO include scrobble api call
|
51
|
+
end
|
52
|
+
|
53
|
+
# Don't play this track again.
|
54
|
+
def ban
|
55
|
+
@session.track_facade.love(name, artist.name)
|
56
|
+
# TODO include scrobble api call
|
57
|
+
end
|
58
|
+
|
59
|
+
# Can this track be streamed
|
60
|
+
def streamable?
|
61
|
+
@streamable
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def identifiers
|
67
|
+
mbid && ! mbid.empty?() ? {:mbid => mbid} : {:artist => artist.name, :track => name}
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Shortwave
|
2
|
+
module Model
|
3
|
+
class User < BaseModel
|
4
|
+
element :name, String
|
5
|
+
element :id, Integer
|
6
|
+
element :real_name, String, :tag => "realname"
|
7
|
+
element :url, String
|
8
|
+
element :image, String
|
9
|
+
element :language, String, :tag => "lang"
|
10
|
+
element :country, String
|
11
|
+
element :age, Integer
|
12
|
+
element :gender, String
|
13
|
+
element :subscriber, Boolean
|
14
|
+
element :play_count, Integer, :tag => "playcount"
|
15
|
+
|
16
|
+
identified_by :name
|
17
|
+
shoutable
|
18
|
+
include WeeklyCharts
|
19
|
+
|
20
|
+
# Returns the events that this user is attending.
|
21
|
+
def events
|
22
|
+
link :events, :Event, name
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns the events a user has attended.
|
26
|
+
def past_events
|
27
|
+
link :past_events, :Event, name
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns the recent tracks played by this user.
|
31
|
+
def recent_tracks
|
32
|
+
link :recent_tracks, :Track, name
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns tracks a user has "loved".
|
36
|
+
def loved_tracks
|
37
|
+
link :loved_tracks, :Track, name
|
38
|
+
end
|
39
|
+
|
40
|
+
# Returns other Last.fm users with similar taste to this user.
|
41
|
+
def neighbours
|
42
|
+
link :neighbours, :User, name
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns the first 50 of a user's friends on Last.fm
|
46
|
+
def friends
|
47
|
+
link :friends, :User, name
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns a user's playlists
|
51
|
+
def playlists
|
52
|
+
link :playlists, :Playlist, name
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns events this user may be interested in.
|
56
|
+
def recommended_events
|
57
|
+
link :recommended_events, :Event
|
58
|
+
end
|
59
|
+
|
60
|
+
# Returns the first 50 artists this user may be interested in.
|
61
|
+
def recommended_artists
|
62
|
+
link :recommended_artists, :Artist
|
63
|
+
end
|
64
|
+
|
65
|
+
def compare_with(other_user)
|
66
|
+
Comparison.parse(@session.tasteometer_facacde.compare("user", "user", name, other_user.name), :single => true)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module Shortwave
|
4
|
+
module Model
|
5
|
+
# A venue that hosts events.
|
6
|
+
#
|
7
|
+
# === Attributes
|
8
|
+
#
|
9
|
+
# +id+:: Last.fm id
|
10
|
+
# +name+:: Venue name
|
11
|
+
# +url+:: Venue url on Last.fm
|
12
|
+
# +location+:: Venue's location
|
13
|
+
# +city+:: Venue city
|
14
|
+
# +street_address+:: Venue street address
|
15
|
+
# +country+:: Venue country
|
16
|
+
# +postcode+:: Venue post code
|
17
|
+
class Venue < BaseModel
|
18
|
+
extend Forwardable
|
19
|
+
def_delegators(:location, :city, :street_address, :country, :postcode)
|
20
|
+
|
21
|
+
element :id, Integer
|
22
|
+
element :name, String
|
23
|
+
element :url, String
|
24
|
+
has_one :location, "Shortwave::Model::Location"
|
25
|
+
|
26
|
+
# Returns events showing at this venue.
|
27
|
+
def events
|
28
|
+
link :events, :Event, id
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns past events from this venue.
|
32
|
+
def past_events
|
33
|
+
link :past_events, :Event, id
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Shortwave
|
2
|
+
module Model
|
3
|
+
module WeeklyCharts
|
4
|
+
# Returns the most popular albums from this group for the last week
|
5
|
+
def album_chart
|
6
|
+
link :weekly_album_chart, "Album", name
|
7
|
+
end
|
8
|
+
|
9
|
+
# Returns the most popular artists from this group for the last week
|
10
|
+
def artist_chart
|
11
|
+
link :weekly_artist_chart, "Artist", name
|
12
|
+
end
|
13
|
+
|
14
|
+
# Returns the most popular tracks from this group for the last week
|
15
|
+
def track_chart
|
16
|
+
link :weekly_track_chart, "Track", name
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns an array of Time ranges, repesenting the weeks charts are available
|
20
|
+
def chart_dates
|
21
|
+
@chart_dates ||= charts.map {|cd| cd.from..cd.to }.reverse
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def charts
|
27
|
+
@charts ||= link :weekly_chart_list, "ChartDates", name
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
module Shortwave
|
2
|
+
module Provider
|
3
|
+
# Intended to be mixed in to authentication classes
|
4
|
+
module ProviderMethods
|
5
|
+
[:album, :artist, :group, :location, :playlist, :track, :tag, :user, :venue].each do |name|
|
6
|
+
klass_name = name.to_s.capitalize
|
7
|
+
module_eval <<-EOV
|
8
|
+
def #{name}
|
9
|
+
@#{name}_provider ||= Provider::#{klass_name}Provider.new(#{name}_facade)
|
10
|
+
end
|
11
|
+
|
12
|
+
def #{name}_facade
|
13
|
+
@#{name}_facade ||= Facade::#{klass_name}.new(self)
|
14
|
+
end
|
15
|
+
EOV
|
16
|
+
end
|
17
|
+
|
18
|
+
def event_facade
|
19
|
+
@event_facade ||= Facade::Event.new(self)
|
20
|
+
end
|
21
|
+
|
22
|
+
def location_facade
|
23
|
+
@location_facade ||= Facade::Geo.new(self)
|
24
|
+
end
|
25
|
+
|
26
|
+
def tasteometer_facacde
|
27
|
+
@tasteometer_facade ||= Facade::Tasteometer.new(self)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
# You should generally use an instance of this class provided by a session:
|
33
|
+
# session.album
|
34
|
+
class BaseProvider
|
35
|
+
# Creates a provider with a facade
|
36
|
+
def initialize(facade)
|
37
|
+
@klass = Model.const_get(self.class.name.split("::").last.sub("Provider",''))
|
38
|
+
@facade = facade
|
39
|
+
end
|
40
|
+
|
41
|
+
# Builds a model object.
|
42
|
+
def build(attributes)
|
43
|
+
model = @klass.new
|
44
|
+
attributes.each {|attr, value| model.send("#{attr}=".to_sym, value) }
|
45
|
+
model.session = @facade.session
|
46
|
+
model
|
47
|
+
end
|
48
|
+
|
49
|
+
protected
|
50
|
+
|
51
|
+
# Defines a search method on this provider
|
52
|
+
def self.searchable
|
53
|
+
define_method :search do |name|
|
54
|
+
parse_collection @facade.search(name)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.identifiable_by_mbid
|
59
|
+
define_method :get_by_id do |mbid|
|
60
|
+
mbid = mbid.uuid if mbid.respond_to? :uuid
|
61
|
+
parse_model @facade.info(:mbid => mbid)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Parses an xml response into a Shortwave::Model::* object
|
66
|
+
def parse_model(response)
|
67
|
+
model = @klass.parse(response, :single => true)
|
68
|
+
model.session = @facade.session
|
69
|
+
model
|
70
|
+
end
|
71
|
+
|
72
|
+
# Parses an xml response into an array of Shortwave::Model::* objects
|
73
|
+
def parse_collection(response)
|
74
|
+
@klass.parse(response).each {|model| model.session = @facade.session }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class PlaylistProvider < BaseProvider
|
79
|
+
def create(title, description=nil)
|
80
|
+
hsh = description ? {:description => description} : {}
|
81
|
+
parse_model @facade.create(hsh.merge(:title => title))
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Produces album objects.
|
86
|
+
class AlbumProvider < BaseProvider
|
87
|
+
searchable
|
88
|
+
identifiable_by_mbid
|
89
|
+
|
90
|
+
# Gets an album, given an artist name and an album name
|
91
|
+
def get(artist, name)
|
92
|
+
parse_model @facade.info(:artist => artist, :album => name)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
# Produces artist objects.
|
98
|
+
class ArtistProvider < BaseProvider
|
99
|
+
searchable
|
100
|
+
identifiable_by_mbid
|
101
|
+
|
102
|
+
def get(artist)
|
103
|
+
parse_model @facade.info(:artist => artist)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
class GroupProvider < BaseProvider
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
# Locations can only be built
|
113
|
+
class LocationProvider < BaseProvider
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
# Produces Tag objects
|
118
|
+
class TagProvider < BaseProvider
|
119
|
+
searchable
|
120
|
+
|
121
|
+
# Returns a tag named "name"
|
122
|
+
def get(name)
|
123
|
+
parse_model @facade.search(name)
|
124
|
+
end
|
125
|
+
|
126
|
+
# Returns the most popular tags from Last.fm
|
127
|
+
def popular
|
128
|
+
parse_collection @facade.top_tags
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
|
133
|
+
# Produces track objects.
|
134
|
+
class TrackProvider < BaseProvider
|
135
|
+
searchable
|
136
|
+
identifiable_by_mbid
|
137
|
+
|
138
|
+
# Gets a track, given an artist name and a track name
|
139
|
+
def get(artist, name)
|
140
|
+
parse_model @facade.info(:artist => artist, :track => name)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
|
145
|
+
# Produces Venue objects
|
146
|
+
class VenueProvider < BaseProvider
|
147
|
+
def search(name, country=nil)
|
148
|
+
hsh = {}
|
149
|
+
hsh[:country] = country if country
|
150
|
+
parse_collection @facade.search(name, hsh)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
|
155
|
+
class UserProvider < BaseProvider
|
156
|
+
def logged_in_user
|
157
|
+
parse_model @facade.info
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|