monkeymusic 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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"