mumbletune 0.1.3 → 0.2.1
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.
- data/.gitignore +3 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +42 -49
- data/README.md +11 -10
- data/bin/mumbletune-ctl +23 -0
- data/conf.example.yaml +3 -4
- data/docs/commands.md +6 -3
- data/lib/mumbletune.rb +40 -35
- data/lib/mumbletune/collection.rb +25 -5
- data/lib/mumbletune/hallon_player.rb +162 -0
- data/lib/mumbletune/messages.rb +24 -19
- data/lib/mumbletune/mumble_client.rb +28 -9
- data/lib/mumbletune/resolver.rb +19 -32
- data/lib/mumbletune/spotify_resolver.rb +51 -0
- data/lib/mumbletune/{templates → template}/commands.mustache +4 -3
- data/lib/mumbletune/{templates → template}/queue.mustache +13 -11
- data/lib/mumbletune/version.rb +1 -1
- data/logs/.gitignore +4 -0
- data/mumbletune.gemspec +5 -10
- metadata +18 -82
- data/lib/mumbletune/handle_sp_error.rb +0 -23
- data/lib/mumbletune/mpd_client.rb +0 -171
- data/lib/mumbletune/sp_uri_server.rb +0 -42
- data/lib/mumbletune/spotify_track.rb +0 -90
- data/lib/mumbletune/track.rb +0 -32
@@ -1,23 +0,0 @@
|
|
1
|
-
module Mumbletune
|
2
|
-
|
3
|
-
# The Spotify Metadata API seems to throw 502 (Bad Gateway) errors *a lot*.
|
4
|
-
# There's some complaint about this online, and I can reproduce it with
|
5
|
-
# plenty of REST clients, so I am sure it's Spotify's problem. But they
|
6
|
-
# don't seem too interested in fixing what appears to be a long-standing
|
7
|
-
# issue. Ho hum.
|
8
|
-
def self.handle_sp_error
|
9
|
-
begin
|
10
|
-
yield
|
11
|
-
rescue MetaSpotify::ServerError => err
|
12
|
-
puts "Caught ServerError: #{err}"
|
13
|
-
failed ||= 0
|
14
|
-
failed += 1
|
15
|
-
if failed < 4
|
16
|
-
sleep 1
|
17
|
-
retry
|
18
|
-
else
|
19
|
-
raise
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
@@ -1,171 +0,0 @@
|
|
1
|
-
require 'ruby-mpd'
|
2
|
-
|
3
|
-
module Mumbletune
|
4
|
-
|
5
|
-
class Player
|
6
|
-
|
7
|
-
attr_accessor :history
|
8
|
-
|
9
|
-
def initialize(host=Mumbletune.config['mpd']['host'], port=Mumbletune.config['mpd']['port'])
|
10
|
-
@mpd = MPD.new(host, port)
|
11
|
-
self.connect
|
12
|
-
self.clear_all
|
13
|
-
|
14
|
-
@history = Array.new
|
15
|
-
@prev_id = 0
|
16
|
-
|
17
|
-
self.defaults
|
18
|
-
self.establish_callbacks
|
19
|
-
end
|
20
|
-
|
21
|
-
def connect
|
22
|
-
@disconnecting = false
|
23
|
-
@mpd.connect true # 'true' enables callbacks
|
24
|
-
end
|
25
|
-
|
26
|
-
def disconnect
|
27
|
-
@disconnecting = true
|
28
|
-
@mpd.disconnect
|
29
|
-
puts ">> Disconnected from MPD"
|
30
|
-
end
|
31
|
-
|
32
|
-
# Setup
|
33
|
-
|
34
|
-
def defaults
|
35
|
-
@mpd.volume = Mumbletune.config["mpd"]["default_volume"] || 100
|
36
|
-
@mpd.consume = true
|
37
|
-
end
|
38
|
-
|
39
|
-
def establish_callbacks
|
40
|
-
@mpd.on :connection do |status|
|
41
|
-
if status == false && !@disconnecting
|
42
|
-
self.connect
|
43
|
-
else
|
44
|
-
puts ">> MPD happens to be connected."
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
# Fires when currently playing song changes.
|
49
|
-
@mpd.on :songid do |id|
|
50
|
-
|
51
|
-
# Clear old tracks from the store.
|
52
|
-
Track.store.delete_if { |t| t.mpd_id == @prev_id }
|
53
|
-
|
54
|
-
@prev_id = id
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
# Status methods
|
59
|
-
|
60
|
-
def playing?
|
61
|
-
state = @mpd.status[:state]
|
62
|
-
if state =~ /^(play|pause)$/i
|
63
|
-
true
|
64
|
-
else
|
65
|
-
false
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
def paused?
|
70
|
-
state = @mpd.status[:state]
|
71
|
-
if state == :pause
|
72
|
-
true
|
73
|
-
else
|
74
|
-
false
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
|
79
|
-
# MPD Settings
|
80
|
-
|
81
|
-
def volume?
|
82
|
-
@mpd.volume
|
83
|
-
end
|
84
|
-
|
85
|
-
def volume(percent)
|
86
|
-
@mpd.volume = percent
|
87
|
-
end
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
# Queue
|
92
|
-
|
93
|
-
def add_collection(col, now=false)
|
94
|
-
col.tracks.each do |t|
|
95
|
-
id = @mpd.addid t.url,
|
96
|
-
(now) ? col.tracks.index(t)+1 : nil
|
97
|
-
t.mpd_id = id
|
98
|
-
end
|
99
|
-
|
100
|
-
@history.push col
|
101
|
-
|
102
|
-
@mpd.next if now
|
103
|
-
end
|
104
|
-
|
105
|
-
def queue
|
106
|
-
# Combine the future queue with the current track.
|
107
|
-
# MPD puts the current track in its queue only if
|
108
|
-
# other tracks are queued to play. Account for this.
|
109
|
-
queue = @mpd.queue
|
110
|
-
queue.unshift @mpd.current_song if @mpd.current_song
|
111
|
-
|
112
|
-
# Delete index 0 if first queue position and current song are duplicates.
|
113
|
-
queue.delete_at(0) if queue[1] && queue[0] == queue[1]
|
114
|
-
|
115
|
-
# Associate known Tracks with Queue items.
|
116
|
-
mapped_queue = queue.map do |mpd_song|
|
117
|
-
t = Track.retreive_from_mpd_id(mpd_song.id)
|
118
|
-
if t
|
119
|
-
t.queue_pos = mpd_song.pos
|
120
|
-
t
|
121
|
-
end
|
122
|
-
end
|
123
|
-
mapped_queue
|
124
|
-
end
|
125
|
-
|
126
|
-
def current_song
|
127
|
-
Track.retreive_from_mpd_id(@mpd.current_song.id) if @mpd.playing?
|
128
|
-
end
|
129
|
-
|
130
|
-
def undo
|
131
|
-
last_collection = @history.pop
|
132
|
-
|
133
|
-
last_collection.tracks.each do |t|
|
134
|
-
to_delete = @mpd.queue.select { |mpd_song| mpd_song.id == t.mpd_id }.first
|
135
|
-
@mpd.delete(to_delete.pos) if to_delete
|
136
|
-
end
|
137
|
-
last_collection
|
138
|
-
end
|
139
|
-
|
140
|
-
def clear_all
|
141
|
-
@mpd.clear
|
142
|
-
end
|
143
|
-
|
144
|
-
def clear_queue
|
145
|
-
current = @mpd.current_song
|
146
|
-
@mpd.queue.each do |t|
|
147
|
-
@mpd.delete :id => t.id unless t.id == current.id
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
# Playback commands
|
152
|
-
|
153
|
-
def play
|
154
|
-
@mpd.play
|
155
|
-
end
|
156
|
-
|
157
|
-
def pause
|
158
|
-
@mpd.pause = (@mpd.playing?) ? true : false
|
159
|
-
end
|
160
|
-
|
161
|
-
def next
|
162
|
-
@mpd.next
|
163
|
-
end
|
164
|
-
|
165
|
-
def stop
|
166
|
-
@mpd.stop
|
167
|
-
end
|
168
|
-
|
169
|
-
end
|
170
|
-
|
171
|
-
end
|
@@ -1,42 +0,0 @@
|
|
1
|
-
require 'sinatra/base'
|
2
|
-
|
3
|
-
module Mumbletune
|
4
|
-
|
5
|
-
module SPURIServer
|
6
|
-
|
7
|
-
class Server < Sinatra::Base
|
8
|
-
set :run, true
|
9
|
-
set :server, :thin
|
10
|
-
set :logging, false
|
11
|
-
set :app_file, __FILE__
|
12
|
-
set :bind, 'localhost'
|
13
|
-
set :port, 8081
|
14
|
-
|
15
|
-
get '/play/:uri' do
|
16
|
-
cred = Mumbletune.config['spotify']
|
17
|
-
sp = Mumbletune::Spotify.new(cred['username'], cred['password'])
|
18
|
-
|
19
|
-
track = sp.objectFromURI(params[:uri])
|
20
|
-
halt 404, "Could not find a track with that URI." if track == nil
|
21
|
-
|
22
|
-
url = track.getFileURL().to_s
|
23
|
-
halt 404, "Could not find a track URL for that URI." if track == nil
|
24
|
-
|
25
|
-
sp.logout
|
26
|
-
redirect url, 303
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def self.url_for(uri)
|
31
|
-
bind = Server::bind
|
32
|
-
port = Server::port
|
33
|
-
"http://#{bind}:#{port}/play/#{uri}"
|
34
|
-
end
|
35
|
-
|
36
|
-
def self.sp_uri_for(url)
|
37
|
-
regexp = /(.+)(<sp_uri>spotify:\w+:\w+)/i
|
38
|
-
matched = regexp.match(url)
|
39
|
-
matched[:sp_uri]
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
@@ -1,90 +0,0 @@
|
|
1
|
-
require 'uri'
|
2
|
-
|
3
|
-
module Mumbletune
|
4
|
-
|
5
|
-
class SpotifyTrack < Track
|
6
|
-
def initialize(params)
|
7
|
-
super
|
8
|
-
|
9
|
-
@uri = params[:uri]
|
10
|
-
@url = SPURIServer.url_for(@uri)
|
11
|
-
end
|
12
|
-
|
13
|
-
def self.track_from_uri(track)
|
14
|
-
track = MetaSpotify::Track.lookup(track) unless track.class == MetaSpotify::Track
|
15
|
-
|
16
|
-
# force track to be playable within region
|
17
|
-
unless track.album.available_territories.include? Mumbletune.config["spotify"]["region"]
|
18
|
-
raise "#{track.name}: Not available in this region."
|
19
|
-
end
|
20
|
-
|
21
|
-
song = SpotifyTrack.new({
|
22
|
-
:name => track.name,
|
23
|
-
:artist => track.artists.first.name,
|
24
|
-
:album => track.album.name,
|
25
|
-
:uri => track.uri
|
26
|
-
})
|
27
|
-
|
28
|
-
# Technically, a collection of one.
|
29
|
-
Collection.new(
|
30
|
-
:TRACK,
|
31
|
-
song,
|
32
|
-
"<b>#{song.name}</b> by <b>#{song.artist}</b>"
|
33
|
-
)
|
34
|
-
end
|
35
|
-
|
36
|
-
def self.tracks_from_album(album_ref)
|
37
|
-
album_uri = album_ref.uri if album_ref.class == MetaSpotify::Album
|
38
|
-
album = MetaSpotify::Album.lookup(album_uri, {:extras => "track"})
|
39
|
-
|
40
|
-
# force album to be playable in region
|
41
|
-
unless album.available_territories.include? Mumbletune.config["spotify"]["region"]
|
42
|
-
raise "#{album.name}: Not available in this region."
|
43
|
-
end
|
44
|
-
|
45
|
-
tracks = []
|
46
|
-
album.tracks.each do |track|
|
47
|
-
tracks.push SpotifyTrack.new({
|
48
|
-
:name => track.name,
|
49
|
-
:artist => track.artists.first.name,
|
50
|
-
:album => album.name,
|
51
|
-
:uri => track.uri
|
52
|
-
})
|
53
|
-
end
|
54
|
-
|
55
|
-
Collection.new(
|
56
|
-
:ALBUM,
|
57
|
-
tracks,
|
58
|
-
"the album <b>#{album.name}</b> by <b>#{album.artists.first.name}</b>"
|
59
|
-
)
|
60
|
-
end
|
61
|
-
|
62
|
-
def self.tracks_from_artist(artist)
|
63
|
-
artist = MetaSpotify::Artist.lookup(artist) unless artist.class == MetaSpotify::Artist
|
64
|
-
|
65
|
-
# spotify metadata api still error-prone
|
66
|
-
search_result = Mumbletune.handle_sp_error { MetaSpotify::Track.search("artist:\"#{artist.name}\"") }
|
67
|
-
|
68
|
-
# filter out tracks outside region
|
69
|
-
search_result[:tracks].select! do |track|
|
70
|
-
track.album.available_territories.include? Mumbletune.config["spotify"]["region"]
|
71
|
-
end
|
72
|
-
|
73
|
-
tracks = []
|
74
|
-
search_result[:tracks][0...10].each do |track|
|
75
|
-
tracks.push SpotifyTrack.new({
|
76
|
-
:name => track.name,
|
77
|
-
:artist => track.artists.first.name,
|
78
|
-
:album => track.album.name,
|
79
|
-
:uri => track.uri
|
80
|
-
})
|
81
|
-
end
|
82
|
-
|
83
|
-
Collection.new(
|
84
|
-
:ARTIST_TOP,
|
85
|
-
tracks,
|
86
|
-
"#{tracks.length} tracks by <b>#{artist.name}</b>"
|
87
|
-
)
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
data/lib/mumbletune/track.rb
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
module Mumbletune
|
2
|
-
class Track
|
3
|
-
attr_accessor :name, :artist, :album, :url, :mpd_id, :queue_pos
|
4
|
-
|
5
|
-
class << self
|
6
|
-
attr_accessor :store
|
7
|
-
end
|
8
|
-
self.store = []
|
9
|
-
|
10
|
-
def initialize(params)
|
11
|
-
@name = params[:name]
|
12
|
-
@artist = params[:artist]
|
13
|
-
@album = params[:album]
|
14
|
-
@url = params[:url]
|
15
|
-
|
16
|
-
Track.store.push self
|
17
|
-
end
|
18
|
-
|
19
|
-
def playing?
|
20
|
-
if self == Mumbletune.player.current_song
|
21
|
-
true
|
22
|
-
else
|
23
|
-
false
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def self.retreive_from_mpd_id(id)
|
28
|
-
Track.store.select { |t| t.mpd_id == id }.first
|
29
|
-
end
|
30
|
-
|
31
|
-
end
|
32
|
-
end
|