monkeymusic 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  require 'hallon'
2
2
 
3
- module MonkeyMusic
3
+ module MonkeyMusic::Generate
4
4
  class RecommendationLoader
5
5
 
6
6
  def initialize(loaded_toplists, load_factor = 5)
@@ -10,66 +10,30 @@ module MonkeyMusic
10
10
 
11
11
  def load_for_user!(user)
12
12
  @user = user
13
- puts "Loading recommendations from album toplist..."
14
- load_recommendations_from_albums!
15
- puts "Loading recommendations from artist toplist..."
16
- load_recommendations_from_artists!
17
- puts "Loading recommendations from track toplist albums..."
18
- load_recommendations_from_top_track_albums!
19
- puts "Loading recommendations from track toplist..."
20
- load_recommendations_from_already_heard!
21
- puts "Loading recommendations from disliked artists..."
22
- load_recommendations_from_disliked!
23
- end
24
-
25
- private
26
-
27
- def load_recommendations_from_albums!
28
- @loaded_toplists[:top_albums].each do |album|
29
- puts "Browsing #{album.name}..."
30
- browse = album.browse
31
- browse.load unless browse.loaded?
32
- browse.tracks.first(@load_factor).each do |rec|
33
- @user.recommendations << parse_track(rec)
34
- end
13
+ @loaded_toplists.each do |name, list|
14
+ puts "Loading #{name}..."
15
+ list[:items].each { |item| load_recommendations_for!(list[:type], item) }
35
16
  end
36
17
  end
37
18
 
38
- def load_recommendations_from_artists!
39
- @loaded_toplists[:top_artists].each do |artist|
40
- puts "Browsing #{artist.name}..."
41
- browse = artist.browse(:no_albums)
42
- browse.load unless browse.loaded?
43
- browse.top_hits.first(@load_factor).each do |rec|
44
- @user.recommendations << parse_track(rec)
45
- end
46
- end
47
- end
48
-
49
- def load_recommendations_from_top_track_albums!
50
- @loaded_toplists[:top_track_albums].each do |album|
51
- puts "Browsing #{album.name}..."
52
- browse = album.browse
53
- browse.load unless browse.loaded?
54
- browse.tracks.first(@load_factor).each do |rec|
55
- @user.recommendations << parse_track(rec)
56
- end
57
- end
58
- end
59
-
60
- def load_recommendations_from_already_heard!
61
- @loaded_toplists[:top_tracks].each do |track|
62
- @user.recommendations << parse_track(track)
63
- end
64
- end
19
+ private
65
20
 
66
- def load_recommendations_from_disliked!
67
- @loaded_toplists[:disliked].each do |artist|
68
- puts "Browsing #{artist.name}..."
69
- browse = artist.browse(:no_albums)
21
+ def load_recommendations_for!(type, item)
22
+ if type == :tracks
23
+ @user.recommendations << parse_track(item)
24
+ else
25
+ browse = case type
26
+ when :artists then item.browse(:no_albums)
27
+ when :albums then item.browse
28
+ end
29
+ puts "Browsing #{item.name}..."
70
30
  browse.load unless browse.loaded?
71
- browse.top_hits.first(@load_factor).each do |rec|
72
- @user.recommendations << parse_track(rec)
31
+ collection = case type
32
+ when :artists then browse.top_hits
33
+ when :albums then browse.tracks
34
+ end
35
+ collection.first(@load_factor).each do |track|
36
+ @user.recommendations << parse_track(track)
73
37
  end
74
38
  end
75
39
  end
@@ -77,14 +41,11 @@ module MonkeyMusic
77
41
  def parse_track(track)
78
42
  album = track.album.load
79
43
  artist = track.artist.load
80
- user_track = Track.new
81
- user_track.uri = track.to_link.to_str
82
- user_track.name = track.name
83
- user_track.artist = artist.name
84
- user_track.album = album.name
85
- user_track.popularity = track.popularity
86
- user_track.year = album.release_year
87
- user_track
44
+ MonkeyMusic::Metadata::Track.new :uri => track.to_link.to_str,
45
+ :name => track.name,
46
+ :artist => artist.name,
47
+ :album => album.name,
48
+ :year => album.release_year
88
49
  end
89
50
 
90
51
  end
@@ -0,0 +1,100 @@
1
+ require 'optparse'
2
+ require 'hallon'
3
+
4
+ module MonkeyMusic
5
+ module Generate
6
+ class Runner
7
+
8
+ def initialize
9
+ OptionParser.new do |opts|
10
+ opts.banner = 'Usage: monkeymusic-generate [options]'
11
+
12
+ opts.on('-g',
13
+ '--generate TOPLIST_FILE',
14
+ 'Generate a user from a toplist file.') do |user|
15
+ @toplist_file = File.join(Dir.getwd, user)
16
+ end
17
+
18
+ opts.on('-o',
19
+ '--out OUT_FILE',
20
+ 'The file to dump a generated user to.') do |file|
21
+ @out_file = File.join(Dir.getwd, file)
22
+ end
23
+
24
+ opts.on('-k',
25
+ '--app-key KEY',
26
+ 'Path to libspotify application key.') do |key|
27
+ @spotify_appkey_file = File.join(Dir.getwd, key)
28
+ end
29
+
30
+ opts.on('-a',
31
+ '--account ACCOUNT',
32
+ 'Username for a Spotify premium account.') do |account|
33
+ @spotify_account = account
34
+ end
35
+
36
+ opts.on('-w', '--password PASSWORD',
37
+ 'Password for a Spotify premium account.') do |password|
38
+ @spotify_password = password
39
+ end
40
+
41
+ opts.on_tail('-h',
42
+ '--help',
43
+ 'Show this message.') do
44
+ puts opts
45
+ exit
46
+ end
47
+ end.parse!
48
+ end
49
+
50
+ def run
51
+ if generate_user?
52
+ # Create user
53
+ user = User.new
54
+ # Connect to libspotify
55
+ Hallon.load_timeout = 0
56
+ session = Hallon::Session.initialize(IO.read(@spotify_appkey_file))
57
+ session.login!(@spotify_account, @spotify_password)
58
+ # Load toplists
59
+ puts "Loading toplists from #{@toplist_file}..."
60
+ toplist_loader = ToplistLoader.new(@toplist_file)
61
+ toplist_loader.load_for_user!(user)
62
+ # Generate recommendations
63
+ puts "Loading recommendations..."
64
+ loaded_toplists = toplist_loader.loaded_toplists
65
+ recommendation_loader = RecommendationLoader.new(loaded_toplists)
66
+ recommendation_loader.load_for_user!(user)
67
+ ## Disconnect from libspotify
68
+ session.logout!
69
+ # Evaluate recommendations
70
+ puts "Evaluating recommendations..."
71
+ score_system = ScoreSystem.new
72
+ score_system.evaluate_user_recommendations!(user)
73
+ # Dump and print the user
74
+ File.open(@out_file, 'w') do |f|
75
+ f.write(user.dump)
76
+ end
77
+ puts "====="
78
+ puts "DONE!"
79
+ puts "====="
80
+ #puts "Loaded tracks:"
81
+ #user.recommendations.group_by(&:multiplier).sort.each do |k,v|
82
+ #puts "#Multiplier #{k}:\t#{v.length}"
83
+ #end
84
+ exit
85
+ end
86
+ end
87
+
88
+ private
89
+
90
+ def generate_user?
91
+ (defined? @toplist_file) &&
92
+ (defined? @out_file) &&
93
+ (defined? @spotify_appkey_file) &&
94
+ (defined? @spotify_account) &&
95
+ (defined? @spotify_password)
96
+ end
97
+
98
+ end
99
+ end
100
+ end
@@ -1,34 +1,49 @@
1
- module MonkeyMusic
1
+ module MonkeyMusic::Generate
2
2
  class ScoreSystem
3
3
 
4
4
  def evaluate_user_recommendations!(user)
5
5
  @user = user
6
- @user.recommendations.each { |track| evaluate!(track) }
6
+ @top_decade = calc_top_decade(user)
7
+ @user.recommendations.each {|track| evaluate!(track) }
8
+ puts "The user's top decade is: #{@top_decade}."
7
9
  end
8
10
 
9
11
  def evaluate!(track)
10
12
  multiplier = 0
11
- if @user.toplists[:top_tracks].include? track.name
13
+ if @user.toplists[:top_tracks].any? {|t| t.name == track.name }
12
14
  multiplier = -1
13
- elsif @user.toplists[:disliked].include? track.artist
15
+ elsif @user.toplists[:disliked_artists].any? {|ar| ar.name == track.artist }
14
16
  multiplier = -2
15
17
  else
16
- if @user.toplists[:top_albums].include? track.album
18
+ if @user.toplists[:top_albums].any? {|al| al.name == track.album }
17
19
  multiplier += 1
18
20
  end
19
- if @user.toplists[:top_artists].include? track.artist
21
+ if @user.toplists[:top_artists].any? {|ar| ar.name == track.artist }
20
22
  multiplier += 1
21
23
  end
22
- if @user.toplists[:top_track_albums].include? track.album
24
+ if @top_decade == decade_of(track.year)
23
25
  multiplier += 1
24
26
  end
25
27
  end
26
- track.multiplier = multiplier
27
- track.value = 4**multiplier.abs * sign(multiplier)
28
+ track.set_value(multiplier, 4**multiplier.abs * sign(multiplier))
28
29
  end
29
30
 
30
31
  private
31
32
 
33
+ def calc_top_decade(user)
34
+ decades = {}
35
+ (user.toplists[:top_tracks] + user.toplists[:top_albums]).each do |t|
36
+ decade = decade_of(t.year)
37
+ decades[decade] ||= 0
38
+ decades[decade] += 1
39
+ end
40
+ (decades.max_by {|k,v| v }).first
41
+ end
42
+
43
+ def decade_of(year)
44
+ (year % 100) / 10
45
+ end
46
+
32
47
  def sign(num)
33
48
  num < 0 ? -1 : 1
34
49
  end
@@ -1,67 +1,83 @@
1
1
  require 'hallon'
2
2
 
3
- module MonkeyMusic
3
+ module MonkeyMusic::Generate
4
4
  class ToplistLoader
5
5
  attr_reader :loaded_toplists
6
6
 
7
7
  def initialize(toplist_file)
8
- @toplist_file = IO.read(toplist_file)
8
+ @toplist_file = toplist_file
9
9
  @toplists = {}
10
+ @loaded_toplists = {}
10
11
  end
11
12
 
12
13
  def load_for_user!(user)
13
14
  @user = user
14
15
  # Load toplists URI:s from file
15
- self.instance_eval(@toplist_file)
16
+ self.instance_eval(IO.read @toplist_file)
16
17
  # Load toplist contents from libspotify
17
18
  load_toplists!
18
- load_top_track_albums!
19
19
  end
20
20
 
21
21
  private
22
22
 
23
23
  # User toplist DSL
24
24
 
25
- def toplist(type, uri)
26
- @toplists[type] = uri
25
+ def toplist(type, name, uri)
26
+ @toplists[name] = { :type => type, :uri => uri }
27
27
  end
28
28
 
29
29
  # Toplist loading
30
30
 
31
31
  def load_toplists!
32
- @loaded_toplists = {}
33
- @toplists.each do |type, uri|
34
- @loaded_toplists[type] = load_from_list(type, uri)
35
- @user.toplists[type] = parse_toplist(@loaded_toplists[type])
32
+ @toplists.each do |name, list|
33
+ @loaded_toplists[name] = {
34
+ :type => list[:type],
35
+ :items => load_from_list(list[:type], list[:uri])
36
+ }
37
+ @user.toplists[name] = parse_toplist(list[:type], @loaded_toplists[name][:items])
36
38
  end
37
39
  end
38
40
 
39
- def load_top_track_albums!
40
- @loaded_toplists[:top_track_albums] =
41
- @loaded_toplists[:top_tracks].map(&:album).each(&:load)
42
- @user.toplists[:top_track_albums] =
43
- parse_toplist(@loaded_toplists[:top_track_albums])
44
- end
45
-
46
41
  def load_from_list(type, uri)
47
42
  playlist = Hallon::Playlist.new(uri).load
48
43
  tracks = playlist.tracks.to_a
49
44
  tracks.each(&:load)
50
- if type == :top_tracks
45
+ if type == :tracks
51
46
  tracks
52
- elsif type == :top_artists || type == :disliked
47
+ elsif type == :artists
53
48
  tracks.map(&:artist).each(&:load)
54
- elsif type == :top_albums
49
+ elsif type == :albums
55
50
  tracks.map(&:album).each(&:load)
56
51
  end
57
52
  end
58
53
 
59
- def parse_toplist(list)
60
- result = []
61
- list.each do |item|
62
- result << item.name
54
+ def parse_toplist(type, list)
55
+ case type
56
+ when :tracks then list.map { |track| load_track track }
57
+ when :albums then list.map { |album| load_album album }
58
+ when :artists then list.map { |artist| load_artist artist }
63
59
  end
64
- result
60
+ end
61
+
62
+ def load_album(album)
63
+ artist = album.artist.load
64
+ MonkeyMusic::Metadata::Album.new :name => album.name,
65
+ :artist => artist.name,
66
+ :year => album.release_year
67
+ end
68
+
69
+ def load_artist(artist)
70
+ MonkeyMusic::Metadata::Artist.new :name => artist.name
71
+ end
72
+
73
+ def load_track(track)
74
+ album = track.album.load
75
+ artist = track.artist.load
76
+ MonkeyMusic::Metadata::Track.new :uri => track.to_link.to_str,
77
+ :name => track.name,
78
+ :artist => artist.name,
79
+ :album => album.name,
80
+ :year => album.release_year
65
81
  end
66
82
 
67
83
  end
@@ -1,6 +1,6 @@
1
1
  $: << File.dirname(__FILE__)
2
2
 
3
- require 'monkey_music_generate/generator'
3
+ require 'monkey_music_generate/runner'
4
4
  require 'monkey_music_generate/toplist_loader'
5
5
  require 'monkey_music_generate/recommendation_loader'
6
6
  require 'monkey_music_generate/score_system'
data/users/synth.rb CHANGED
@@ -1,7 +1,11 @@
1
- toplist :top_tracks, "spotify:user:poscar%21:playlist:4yIhuHviCmTYjcFqunPxsa"
1
+ toplist :tracks, :top_tracks,
2
+ "spotify:user:poscar%21:playlist:4yIhuHviCmTYjcFqunPxsa"
2
3
 
3
- toplist :top_artists, "spotify:user:poscar%21:playlist:6MjfrIbeCKQMVCU9JQNf1u"
4
+ toplist :albums, :top_albums,
5
+ "spotify:user:poscar%21:playlist:6MjfrIbeCKQMVCU9JQNf1u"
4
6
 
5
- toplist :top_albums, "spotify:user:poscar%21:playlist:1ZXPeNHa8YYvY0VzxRdv3q"
7
+ toplist :artists, :top_artists,
8
+ "spotify:user:poscar%21:playlist:1ZXPeNHa8YYvY0VzxRdv3q"
6
9
 
7
- toplist :disliked, "spotify:user:poscar%21:playlist:5IEx3cooSMgrtftvw5B9Ug"
10
+ toplist :artists, :disliked_artists,
11
+ "spotify:user:poscar%21:playlist:5IEx3cooSMgrtftvw5B9Ug"