mumbletune 0.1.3 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,8 +2,6 @@ require 'uri'
2
2
  require 'text'
3
3
  require 'mustache'
4
4
 
5
- Thread.abort_on_exception=true # development only
6
-
7
5
  module Mumbletune
8
6
 
9
7
  class Message
@@ -29,9 +27,6 @@ module Mumbletune
29
27
  else
30
28
  play_now = false
31
29
  end
32
-
33
- # reassurance that it's working
34
- message.respond "I'm searching. Hang tight."
35
30
 
36
31
  collection = Mumbletune.resolve(message.argument)
37
32
 
@@ -49,9 +44,6 @@ module Mumbletune
49
44
  else
50
45
  message.respond_all "#{message.sender.name} added #{collection.description} to the queue."
51
46
  end
52
-
53
- Mumbletune.player.play unless Mumbletune.player.playing?
54
-
55
47
 
56
48
  else # user wants to unpause
57
49
  if Mumbletune.player.paused?
@@ -77,9 +69,13 @@ module Mumbletune
77
69
 
78
70
 
79
71
  when /^next$/i
80
- Mumbletune.player.next
81
- current = Mumbletune.player.current_song
82
- message.respond_all "#{message.sender.name} skipped to #{current.artist} - #{current.name}" if current
72
+ if Mumbletune.player.any?
73
+ Mumbletune.player.next
74
+ current = Mumbletune.player.current_track
75
+ message.respond_all "#{message.sender.name} skipped to #{current.artist.name} - #{current.name}" if current
76
+ else
77
+ message.respond "We're at the end of the queue. Try adding something to play!"
78
+ end
83
79
 
84
80
  when /^clear$/i
85
81
  Mumbletune.player.clear_queue
@@ -96,22 +92,29 @@ module Mumbletune
96
92
 
97
93
  when /^(what|queue)$/i
98
94
  queue = Mumbletune.player.queue
95
+ current = Mumbletune.player.current_track
96
+ template_queue = Array.new
97
+ queue.each do |col|
98
+ template_col = {description: col.description, tracks: Array.new}
99
+ col.tracks.each { |t| template_col[:tracks] << {name: t.name, artist: t.artist.name, playing?: current == t, username: col.user} }
100
+ template_queue << template_col
101
+ end
99
102
 
100
103
  # Now, a template.
101
104
  rendered = Mustache.render Message.template[:queue],
102
- :queue => queue,
105
+ :queue => template_queue,
103
106
  :anything? => (queue.empty?) ? false : true
104
107
  message.respond rendered
105
108
 
106
109
  when /^volume\?$/i
107
- message.respond "The volume is #{Mumbletune.player.volume?}."
110
+ message.respond "The volume is #{Mumbletune.mumble.volume}."
108
111
 
109
112
  when /^volume/i
110
113
  if message.argument.length == 0
111
- message.respond "The volume is #{Mumbletune.player.volume?}."
114
+ message.respond "The volume is #{Mumbletune.mumble.volume}."
112
115
  else
113
- Mumbletune.player.volume(message.argument)
114
- message.respond "Now the volume is #{Mumbletune.player.volume?}."
116
+ Mumbletune.mumble.volume = message.argument
117
+ message.respond "Now the volume is #{Mumbletune.mumble.volume}."
115
118
  end
116
119
 
117
120
  when /^help$/i
@@ -126,8 +129,10 @@ module Mumbletune
126
129
 
127
130
  rescue => err # Catch any command that errored.
128
131
  message.respond "Woah, an error occurred: #{err.message}"
129
- puts "#{err.class}: #{err.message}"
130
- puts err.backtrace
132
+ unless Mumbletune.verbose
133
+ puts "#{err.class}: #{err.message}"
134
+ puts err.backtrace
135
+ end
131
136
  end
132
137
  end
133
138
 
@@ -157,7 +162,7 @@ module Mumbletune
157
162
  end
158
163
 
159
164
  # load templates
160
- Dir.glob(File.dirname(__FILE__) + "/templates/*.mustache").each do |f_path|
165
+ Dir.glob(File.dirname(__FILE__) + "/template/*.mustache").each do |f_path|
161
166
  f = File.open(f_path)
162
167
  Message.template[File.basename(f_path, ".mustache").to_sym] = f.read
163
168
  end
@@ -4,16 +4,16 @@ module Mumbletune
4
4
  class MumbleClient
5
5
 
6
6
  def initialize
7
- opts = Mumbletune.config['mumble']
8
- @cli = Mumble::Client.new(opts['host'], opts['port'], opts['username'], opts['password'])
7
+ m_conf = Mumbletune.config["mumble"]
8
+ format = {rate: 44100, channels: 2} # Format used by spotify
9
+ @cli = Mumble::Client.new(m_conf['host'], m_conf['port'], m_conf['username'], m_conf['password'], format)
9
10
 
10
11
  @cli.on_server_sync do |message| # Once connected.
11
12
  @cli.session = message.session # housekeeping for mumble-ruby
12
- connect_to = @cli.channels.select { |key, hash| hash["name"] == opts['channel'] }.first[1][:name]
13
- @cli.join_channel(connect_to)
13
+ connect_to = @cli.channels.select { |key, hash| hash["name"] == m_conf["channel"] }.first[1][:name]
14
+ @cli.join_channel connect_to
14
15
 
15
16
  @ready = true
16
- puts ">> Connected to Mumble server at #{opts['host']}."
17
17
  end
18
18
 
19
19
  @cli.on_text_message do |data|
@@ -35,15 +35,34 @@ module Mumbletune
35
35
 
36
36
  def stream
37
37
  @ready_wait.join
38
- input = Mumbletune.config['mpd']['fifo_out']
39
38
  Thread.current.priority = 5
40
- puts ">> Streaming to Mumble from #{input}"
41
- @cli.stream_raw_audio(input)
39
+ queue = Mumbletune.player.audio_queue
40
+
41
+ @audio_stream = @cli.stream_from_queue(queue)
42
+
43
+ self.volume = Mumbletune.config["player"]["default_volume"]
42
44
  end
43
45
 
44
46
  def disconnect
47
+ @audio_stream.stop if @audio_stream
45
48
  @cli.disconnect
46
- puts ">> Disconnected from Mumble"
49
+ end
50
+
51
+ def message(users, text)
52
+ users = Array(users) # force into array
53
+ users.each { |u| @cli.text_user(u.session, text) }
54
+ end
55
+
56
+ def broadcast(text)
57
+ @cli.text_channel(@cli.me.channel_id, text)
58
+ end
59
+
60
+ def volume
61
+ (@audio_stream.volume * 100).to_i
62
+ end
63
+
64
+ def volume=(vol)
65
+ @audio_stream.volume = vol.to_i
47
66
  end
48
67
  end
49
68
 
@@ -1,8 +1,6 @@
1
1
  require 'uri'
2
- require 'meta-spotify'
3
2
  require 'text'
4
3
 
5
-
6
4
  module Mumbletune
7
5
  def self.resolve(argument)
8
6
  Resolvers.workers.each do |r|
@@ -56,11 +54,14 @@ module Mumbletune
56
54
  # behave according to URI type
57
55
  case type
58
56
  when "track" # Return this track
59
- SpotifyTrack::track_from_uri(sp_uri)
57
+ obj = Hallon::Track.new(sp_uri)
58
+ SpotifyResolver.track(obj)
60
59
  when "album" # Return all tracks of the album to queue
61
- SpotifyTrack::tracks_from_album(sp_uri)
60
+ obj = Hallon::Album.new(sp_uri)
61
+ SpotifyResolver.tracks_from_album(obj)
62
62
  when "artist" # Return 10 tracks for this artist
63
- SpotifyTrack::tracks_from_artist(sp_uri)
63
+ obj = Hallon::Artist.new(sp_uri)
64
+ SpotifyResolver.tracks_from_artist(obj)
64
65
  end
65
66
  end
66
67
  end
@@ -89,35 +90,21 @@ module Mumbletune
89
90
  region = Mumbletune.config["spotify"]["region"]
90
91
 
91
92
  # determine result based on a type in the first word
93
+ search = Hallon::Search.new(query, artists: 1, albums: 1, tracks: 1).load
92
94
  if first_word =~ /^artist$/i
93
- artist = Mumbletune.handle_sp_error { MetaSpotify::Artist.search(query) }
94
- result = artist[:artists].first
95
+ result = search.artists.first
95
96
 
96
97
  elsif first_word =~ /^album$/i
97
- album = Mumbletune.handle_sp_error { MetaSpotify::Album.search(query) }
98
- album[:albums].select! { |a| a.available_territories.include? region }
99
- result = album[:albums].first
98
+ result = search.albums.first
100
99
 
101
100
  elsif first_word =~ /^track$/i
102
- track = Mumbletune.handle_sp_error { MetaSpotify::Track.search(query) }
103
- track[:tracks].select! { |t| t.album.available_territories.include? region }
104
- result = track[:tracks].first
101
+ result = search.tracks.first
105
102
 
106
103
  else # determine intended result by similarity to the query
107
- artist = Mumbletune.handle_sp_error { MetaSpotify::Artist.search(query) }
108
- album = Mumbletune.handle_sp_error { MetaSpotify::Album.search(query) }
109
- track = Mumbletune.handle_sp_error { MetaSpotify::Track.search(query) }
110
-
111
- # searches now finished
112
-
113
- # remove anything out-of-region
114
- album[:albums].select! { |a| a.available_territories.include? region } if album[:albums].any?
115
- track[:tracks].select! { |t| t.album.available_territories.include? region } if track[:tracks].any?
116
-
117
104
  compare = []
118
- compare.push track[:tracks].first if track[:tracks].any?
119
- compare.push album[:albums].first if album[:albums].any?
120
- compare.push artist[:artists].first if artist[:artists].any?
105
+ compare.push search.tracks.first if search.tracks.any?
106
+ compare.push search.albums.first if search.albums.any?
107
+ compare.push search.artists.first if search.artists.any?
121
108
 
122
109
  white = Text::WhiteSimilarity.new
123
110
  compare.sort! do |a, b|
@@ -134,12 +121,12 @@ module Mumbletune
134
121
  result = compare.first
135
122
  end
136
123
 
137
- if result.class == MetaSpotify::Artist
138
- SpotifyTrack.tracks_from_artist(result)
139
- elsif result.class == MetaSpotify::Album
140
- SpotifyTrack.tracks_from_album(result)
141
- elsif result.class == MetaSpotify::Track
142
- SpotifyTrack.track_from_uri(result)
124
+ if result.class == Hallon::Artist
125
+ SpotifyResolver.tracks_from_artist(result)
126
+ elsif result.class == Hallon::Album
127
+ SpotifyResolver.tracks_from_album(result)
128
+ elsif result.class == Hallon::Track
129
+ SpotifyResolver.track(result)
143
130
  end
144
131
 
145
132
  end
@@ -0,0 +1,51 @@
1
+ require "hallon"
2
+
3
+ module Mumbletune
4
+ class SpotifyResolver
5
+ def self.track(track)
6
+ track.load
7
+
8
+ raise "#{track.name}: Not available in this region." unless track.available?
9
+
10
+ # Technically, a collection of one.
11
+ Collection.new(
12
+ :TRACK,
13
+ track,
14
+ "<b>#{track.name}</b> by <b>#{track.artist.name}</b>"
15
+ )
16
+ end
17
+
18
+ def self.tracks_from_album(album)
19
+ album.load
20
+
21
+ raise "#{album.name}: Not available in this region." unless album.available?
22
+
23
+ browse = album.browse
24
+ browse.load
25
+
26
+ Collection.new(
27
+ :ALBUM,
28
+ browse.tracks.to_a,
29
+ "the album <b>#{album.name}</b> by <b>#{album.artist.name}</b>"
30
+ )
31
+ end
32
+
33
+ def self.tracks_from_artist(artist)
34
+ artist.load
35
+
36
+ tracks_needed = Mumbletune.config["player"]["tracks_for_artist"] || 5
37
+
38
+ search = Hallon::Search.new("artist:\"#{artist.name}\"",
39
+ tracks: tracks_needed,
40
+ artists: 0,
41
+ albums: 0,
42
+ playlists: 0).load
43
+
44
+ Collection.new(
45
+ :ARTIST_TOP,
46
+ search.tracks.to_a,
47
+ "#{search.tracks.size} tracks by <b>#{artist.name}</b>"
48
+ )
49
+ end
50
+ end
51
+ end
@@ -63,6 +63,7 @@
63
63
  </tbody>
64
64
  </table>
65
65
  <p><br> For example:</p>
66
- <pre><code>play coldplay
67
- play nicki minaj starships</code></pre>
68
- <p>Mumbletune uses Spotify to find music.</p>
66
+ <p><code>play coldplay</code> plays the top 5 tracks by Coldplay.</p>
67
+ <p><code>play nicki minaj starships</code> will play the (excellent) song Starships.</p>
68
+ <p>Mumbletune uses Spotify to find and play music. You can <code>play</code> a Spotify URL directly instead of searching, like</p>
69
+ <p><code>play spotify:album:1HjSyGjmLNjRAKgT9t1cna</code></p>
@@ -18,17 +18,19 @@
18
18
  {{/anything?}}
19
19
 
20
20
  {{#queue}}
21
- <tr class="odd">
22
- <td align="left">
23
- {{#playing?}} &#9658; {{/playing?}}
24
- </td>
25
- <td align="left">
26
- {{name}}
27
- </td>
28
- <td align="left">
29
- {{artist}}
30
- </td>
31
- </tr>
21
+ {{#tracks}}
22
+ <tr>
23
+ <td align="left">
24
+ {{#playing?}} <span style="#a5a5a5">►</span> {{/playing?}}
25
+ </td>
26
+ <td align="left">
27
+ {{name}}
28
+ </td>
29
+ <td align="left">
30
+ {{artist}} <small style="color:#a5a5a5">{{username}}</small>
31
+ </td>
32
+ </tr>
33
+ {{/tracks}}
32
34
  {{/queue}}
33
35
 
34
36
  {{#anything?}}
@@ -1,3 +1,3 @@
1
1
  module Mumbletune
2
- VERSION = "0.1.3"
2
+ VERSION = "0.2.1"
3
3
  end
@@ -0,0 +1,4 @@
1
+ # Ignore everything in this directory
2
+ *
3
+ # Except this file
4
+ !.gitignore
@@ -9,9 +9,8 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ["Elliott Williams"]
10
10
  spec.email = ["e@elliottwillia.ms"]
11
11
  spec.description = "Mumbletune connects to a mumble server and allows users to"\
12
- " interact with and play a queue of music. Currently plays"\
13
- " from Spotify alone."
14
- spec.summary = "A mumble server bot that plays music"
12
+ " interact with and play a queue of music from Spotify."
13
+ spec.summary = "A Mumble VOIP bot that plays Spotify."
15
14
  spec.homepage = "http://github.com/elliottwilliams/mumbletune"
16
15
  spec.license = "MIT"
17
16
 
@@ -24,14 +23,10 @@ Gem::Specification.new do |spec|
24
23
  spec.add_development_dependency "rake"
25
24
 
26
25
  spec.add_runtime_dependency "mumble-ruby"
27
- spec.add_runtime_dependency "ruby-mpd"
28
- spec.add_runtime_dependency "sinatra"
29
- spec.add_runtime_dependency "meta-spotify"
30
- spec.add_runtime_dependency "rubypython"
26
+ spec.add_runtime_dependency "hallon"
27
+ spec.add_runtime_dependency "ffi", "~>1.3.0"
31
28
 
32
- spec.add_runtime_dependency "thin"
33
- spec.add_runtime_dependency "eventmachine"
29
+ spec.add_runtime_dependency "daemons"
34
30
  spec.add_runtime_dependency "text"
35
31
  spec.add_runtime_dependency "mustache"
36
- spec.add_development_dependency "debugger"
37
32
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mumbletune
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-03-23 00:00:00.000000000 Z
12
+ date: 2013-06-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -60,7 +60,7 @@ dependencies:
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0'
62
62
  - !ruby/object:Gem::Dependency
63
- name: ruby-mpd
63
+ name: hallon
64
64
  requirement: !ruby/object:Gem::Requirement
65
65
  none: false
66
66
  requirements:
@@ -76,71 +76,23 @@ dependencies:
76
76
  - !ruby/object:Gem::Version
77
77
  version: '0'
78
78
  - !ruby/object:Gem::Dependency
79
- name: sinatra
79
+ name: ffi
80
80
  requirement: !ruby/object:Gem::Requirement
81
81
  none: false
82
82
  requirements:
83
- - - ! '>='
84
- - !ruby/object:Gem::Version
85
- version: '0'
86
- type: :runtime
87
- prerelease: false
88
- version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
- requirements:
91
- - - ! '>='
92
- - !ruby/object:Gem::Version
93
- version: '0'
94
- - !ruby/object:Gem::Dependency
95
- name: meta-spotify
96
- requirement: !ruby/object:Gem::Requirement
97
- none: false
98
- requirements:
99
- - - ! '>='
100
- - !ruby/object:Gem::Version
101
- version: '0'
102
- type: :runtime
103
- prerelease: false
104
- version_requirements: !ruby/object:Gem::Requirement
105
- none: false
106
- requirements:
107
- - - ! '>='
108
- - !ruby/object:Gem::Version
109
- version: '0'
110
- - !ruby/object:Gem::Dependency
111
- name: rubypython
112
- requirement: !ruby/object:Gem::Requirement
113
- none: false
114
- requirements:
115
- - - ! '>='
116
- - !ruby/object:Gem::Version
117
- version: '0'
118
- type: :runtime
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- none: false
122
- requirements:
123
- - - ! '>='
124
- - !ruby/object:Gem::Version
125
- version: '0'
126
- - !ruby/object:Gem::Dependency
127
- name: thin
128
- requirement: !ruby/object:Gem::Requirement
129
- none: false
130
- requirements:
131
- - - ! '>='
83
+ - - ~>
132
84
  - !ruby/object:Gem::Version
133
- version: '0'
85
+ version: 1.3.0
134
86
  type: :runtime
135
87
  prerelease: false
136
88
  version_requirements: !ruby/object:Gem::Requirement
137
89
  none: false
138
90
  requirements:
139
- - - ! '>='
91
+ - - ~>
140
92
  - !ruby/object:Gem::Version
141
- version: '0'
93
+ version: 1.3.0
142
94
  - !ruby/object:Gem::Dependency
143
- name: eventmachine
95
+ name: daemons
144
96
  requirement: !ruby/object:Gem::Requirement
145
97
  none: false
146
98
  requirements:
@@ -187,28 +139,13 @@ dependencies:
187
139
  - - ! '>='
188
140
  - !ruby/object:Gem::Version
189
141
  version: '0'
190
- - !ruby/object:Gem::Dependency
191
- name: debugger
192
- requirement: !ruby/object:Gem::Requirement
193
- none: false
194
- requirements:
195
- - - ! '>='
196
- - !ruby/object:Gem::Version
197
- version: '0'
198
- type: :development
199
- prerelease: false
200
- version_requirements: !ruby/object:Gem::Requirement
201
- none: false
202
- requirements:
203
- - - ! '>='
204
- - !ruby/object:Gem::Version
205
- version: '0'
206
142
  description: Mumbletune connects to a mumble server and allows users to interact with
207
- and play a queue of music. Currently plays from Spotify alone.
143
+ and play a queue of music from Spotify.
208
144
  email:
209
145
  - e@elliottwillia.ms
210
146
  executables:
211
147
  - mumbletune
148
+ - mumbletune-ctl
212
149
  extensions: []
213
150
  extra_rdoc_files: []
214
151
  files:
@@ -219,21 +156,20 @@ files:
219
156
  - README.md
220
157
  - Rakefile
221
158
  - bin/mumbletune
159
+ - bin/mumbletune-ctl
222
160
  - conf.example.yaml
223
161
  - docs/commands.md
224
162
  - lib/mumbletune.rb
225
163
  - lib/mumbletune/collection.rb
226
- - lib/mumbletune/handle_sp_error.rb
164
+ - lib/mumbletune/hallon_player.rb
227
165
  - lib/mumbletune/messages.rb
228
- - lib/mumbletune/mpd_client.rb
229
166
  - lib/mumbletune/mumble_client.rb
230
167
  - lib/mumbletune/resolver.rb
231
- - lib/mumbletune/sp_uri_server.rb
232
- - lib/mumbletune/spotify_track.rb
233
- - lib/mumbletune/templates/commands.mustache
234
- - lib/mumbletune/templates/queue.mustache
235
- - lib/mumbletune/track.rb
168
+ - lib/mumbletune/spotify_resolver.rb
169
+ - lib/mumbletune/template/commands.mustache
170
+ - lib/mumbletune/template/queue.mustache
236
171
  - lib/mumbletune/version.rb
172
+ - logs/.gitignore
237
173
  - mumbletune.gemspec
238
174
  homepage: http://github.com/elliottwilliams/mumbletune
239
175
  licenses:
@@ -259,5 +195,5 @@ rubyforge_project:
259
195
  rubygems_version: 1.8.25
260
196
  signing_key:
261
197
  specification_version: 3
262
- summary: A mumble server bot that plays music
198
+ summary: A Mumble VOIP bot that plays Spotify.
263
199
  test_files: []