mpd 0.17.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +3 -0
- data/bin/mpc.rb +8 -0
- data/lib/mpd.rb +12 -0
- data/lib/mpd/controller.rb +206 -0
- data/lib/mpd/controller/audio.rb +78 -0
- data/lib/mpd/controller/channels.rb +117 -0
- data/lib/mpd/controller/commands.rb +57 -0
- data/lib/mpd/controller/config.rb +35 -0
- data/lib/mpd/controller/current_playlist.rb +110 -0
- data/lib/mpd/controller/database.rb +235 -0
- data/lib/mpd/controller/decoders.rb +54 -0
- data/lib/mpd/controller/do.rb +42 -0
- data/lib/mpd/controller/player.rb +109 -0
- data/lib/mpd/controller/playlists.rb +113 -0
- data/lib/mpd/controller/stats.rb +31 -0
- data/lib/mpd/controller/status.rb +69 -0
- data/lib/mpd/controller/stickers.rb +87 -0
- data/lib/mpd/controller/supported_protocols.rb +36 -0
- data/lib/mpd/controller/supported_tags.rb +36 -0
- data/lib/mpd/controller/toggle.rb +72 -0
- data/lib/mpd/protocol.rb +15 -0
- data/lib/mpd/protocol/command.rb +50 -0
- data/lib/mpd/protocol/command_list.rb +48 -0
- data/lib/mpd/protocol/response.rb +181 -0
- data/lib/mpd/version.rb +15 -0
- data/mpd.gemspec +16 -0
- metadata +71 -0
data/README.md
ADDED
data/bin/mpc.rb
ADDED
data/lib/mpd.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
#--
|
2
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
3
|
+
# Version 2, December 2004
|
4
|
+
#
|
5
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
6
|
+
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
7
|
+
#
|
8
|
+
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
9
|
+
#++
|
10
|
+
|
11
|
+
require 'mpd/controller'
|
12
|
+
require 'mpd/version'
|
@@ -0,0 +1,206 @@
|
|
1
|
+
#--
|
2
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
3
|
+
# Version 2, December 2004
|
4
|
+
#
|
5
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
6
|
+
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
7
|
+
#
|
8
|
+
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
9
|
+
#++
|
10
|
+
|
11
|
+
require 'socket'
|
12
|
+
|
13
|
+
require 'mpd/protocol'
|
14
|
+
|
15
|
+
module MPD
|
16
|
+
|
17
|
+
# This is the main class to manage moc.
|
18
|
+
#
|
19
|
+
# The class also acts as a Socket if needed.
|
20
|
+
class Controller
|
21
|
+
autoload :Do, 'mpd/controller/do'
|
22
|
+
autoload :Database, 'mpd/controller/database'
|
23
|
+
autoload :Stats, 'mpd/controller/stats'
|
24
|
+
autoload :Config, 'mpd/controller/config'
|
25
|
+
autoload :SupportedTags, 'mpd/controller/supported_tags'
|
26
|
+
autoload :SupportedProtocols, 'mpd/controller/supported_protocols'
|
27
|
+
autoload :Commands, 'mpd/controller/commands'
|
28
|
+
autoload :Decoders, 'mpd/controller/decoders'
|
29
|
+
autoload :Audio, 'mpd/controller/audio'
|
30
|
+
autoload :Toggle, 'mpd/controller/toggle'
|
31
|
+
autoload :Player, 'mpd/controller/player'
|
32
|
+
autoload :CurrentPlaylist, 'mpd/controller/current_playlist'
|
33
|
+
autoload :Playlists, 'mpd/controller/playlists.rb'
|
34
|
+
autoload :Status, 'mpd/controller/status'
|
35
|
+
autoload :Channels, 'mpd/controller/channels'
|
36
|
+
autoload :Stickers, 'mpd/controller/stickers'
|
37
|
+
|
38
|
+
attr_reader :path, :host, :port, :version
|
39
|
+
|
40
|
+
def initialize (host = 'localhost', port = 6600)
|
41
|
+
@socket = File.exists?(host) ? UNIXSocket.new(host) : TCPSocket.new(host, port)
|
42
|
+
@version = @socket.readline.chomp.split(' ', 3).last
|
43
|
+
|
44
|
+
if unix?
|
45
|
+
@path = host
|
46
|
+
else
|
47
|
+
@host = host
|
48
|
+
@port = port
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def unix?
|
53
|
+
@socket.is_a? UNIXSocket
|
54
|
+
end
|
55
|
+
|
56
|
+
def tcp?
|
57
|
+
@socket.is_a? TCPSocket
|
58
|
+
end
|
59
|
+
|
60
|
+
def respond_to_missing? (id, include_private = false)
|
61
|
+
@socket.respond_to? id, include_private
|
62
|
+
end
|
63
|
+
|
64
|
+
def method_missing (id, *args, &block)
|
65
|
+
if @socket.respond_to? id
|
66
|
+
return @socket.__send__ id, *args, &block
|
67
|
+
end
|
68
|
+
|
69
|
+
super
|
70
|
+
end
|
71
|
+
|
72
|
+
def do (*args, &block)
|
73
|
+
if block
|
74
|
+
Do.new(self, &block).send
|
75
|
+
else
|
76
|
+
name = args.shift
|
77
|
+
command = Protocol::Command.new(name, args)
|
78
|
+
|
79
|
+
@socket.puts command.to_s
|
80
|
+
|
81
|
+
Protocol::Response.read(self, command)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def do_and_raise_if_needed (*args)
|
86
|
+
response = self.do *args
|
87
|
+
|
88
|
+
if response.is_a?(Protocol::Error)
|
89
|
+
raise response.message
|
90
|
+
end
|
91
|
+
|
92
|
+
response
|
93
|
+
end
|
94
|
+
|
95
|
+
def authenticate (password)
|
96
|
+
self.do :password, password
|
97
|
+
|
98
|
+
self
|
99
|
+
end
|
100
|
+
|
101
|
+
def active?
|
102
|
+
self.do(:ping).success?
|
103
|
+
rescue
|
104
|
+
false
|
105
|
+
end
|
106
|
+
|
107
|
+
def kill!
|
108
|
+
self.do :kill
|
109
|
+
end
|
110
|
+
|
111
|
+
def disconnect!
|
112
|
+
self.do :close
|
113
|
+
end
|
114
|
+
|
115
|
+
def database
|
116
|
+
@database ||= Database.new(self)
|
117
|
+
end
|
118
|
+
|
119
|
+
def stats
|
120
|
+
Stats.new(self)
|
121
|
+
end
|
122
|
+
|
123
|
+
def audio
|
124
|
+
Audio.new(self)
|
125
|
+
end
|
126
|
+
|
127
|
+
def config
|
128
|
+
Config.new(self)
|
129
|
+
end
|
130
|
+
|
131
|
+
def supported_tags
|
132
|
+
SupportedTags.new(self)
|
133
|
+
end
|
134
|
+
|
135
|
+
def supported_protocols
|
136
|
+
SupportedProtocols.new(self)
|
137
|
+
end
|
138
|
+
|
139
|
+
def commands
|
140
|
+
Commands.new(self)
|
141
|
+
end
|
142
|
+
|
143
|
+
def decoders
|
144
|
+
Decoders.new(self)
|
145
|
+
end
|
146
|
+
|
147
|
+
def toggle
|
148
|
+
@toggle ||= Toggle.new(self)
|
149
|
+
end
|
150
|
+
|
151
|
+
def player
|
152
|
+
@player ||= Player.new(self)
|
153
|
+
end
|
154
|
+
|
155
|
+
def playlists
|
156
|
+
@playlists ||= Playlists.new(self)
|
157
|
+
end
|
158
|
+
|
159
|
+
def playlist (name = nil)
|
160
|
+
if name
|
161
|
+
playlists[name]
|
162
|
+
else
|
163
|
+
@playlist ||= CurrentPlaylist.new(self)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def status
|
168
|
+
Status.new(self)
|
169
|
+
end
|
170
|
+
|
171
|
+
def channels
|
172
|
+
@channels ||= Channels.new(self)
|
173
|
+
end
|
174
|
+
|
175
|
+
def stickers
|
176
|
+
@stickers ||= Stickers.new(self)
|
177
|
+
end
|
178
|
+
|
179
|
+
def channel (name)
|
180
|
+
channels[name]
|
181
|
+
end
|
182
|
+
|
183
|
+
def wait
|
184
|
+
self.do(:idle).map(&:last)
|
185
|
+
rescue Interrupt
|
186
|
+
stop_waiting and raise # my undead army
|
187
|
+
end
|
188
|
+
|
189
|
+
def wait_for (*args)
|
190
|
+
self.do(:idle, *args.flatten.compact.uniq).map(&:last)
|
191
|
+
rescue Interrupt
|
192
|
+
stop_waiting and raise # my undead army
|
193
|
+
end
|
194
|
+
|
195
|
+
def stop_waiting
|
196
|
+
self.do :noidle
|
197
|
+
end
|
198
|
+
|
199
|
+
def loop (*what)
|
200
|
+
loop do
|
201
|
+
yield wait_for *what
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
#--
|
2
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
3
|
+
# Version 2, December 2004
|
4
|
+
#
|
5
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
6
|
+
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
7
|
+
#
|
8
|
+
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
9
|
+
#++
|
10
|
+
|
11
|
+
module MPD; class Controller
|
12
|
+
|
13
|
+
class Audio
|
14
|
+
class Output
|
15
|
+
attr_reader :audio, :id
|
16
|
+
|
17
|
+
def initialize (audio, id)
|
18
|
+
@audio = audio
|
19
|
+
@id = id
|
20
|
+
end
|
21
|
+
|
22
|
+
def name
|
23
|
+
audio.controller.do_and_raise_if_needed(:outputs).each_slice(3) {|(_, id), (_, name), (_, enabled)|
|
24
|
+
return name if @id == id
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
def enabled?
|
29
|
+
audio.controller.do_and_raise_if_needed(:outputs).each_slice(3) {|(_, id), (_, name), (_, enabled)|
|
30
|
+
return enabled if @id == id
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
def enable!
|
35
|
+
audio.controller.do_and_raise_if_needed :enableoutput, id
|
36
|
+
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
def disable!
|
41
|
+
audio.controller.do_and_raise_if_needed :disableoutput, id
|
42
|
+
|
43
|
+
self
|
44
|
+
end
|
45
|
+
|
46
|
+
def inspect
|
47
|
+
"#<#{self.class.name}(#{id}, #{enabled? ? 'enabled' : 'disabled'}): #{name}>"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
include Enumerable
|
52
|
+
|
53
|
+
attr_reader :controller
|
54
|
+
|
55
|
+
def initialize (controller)
|
56
|
+
@controller = controller
|
57
|
+
end
|
58
|
+
|
59
|
+
def each
|
60
|
+
return to_enum unless block_given?
|
61
|
+
|
62
|
+
controller.do_and_raise_if_needed(:outputs).each_slice(3) {|(_, id), (_, name), (_, enabled)|
|
63
|
+
yield Output.new(self, id)
|
64
|
+
}
|
65
|
+
|
66
|
+
self
|
67
|
+
end
|
68
|
+
|
69
|
+
def [] (matches)
|
70
|
+
controller.do_and_raise_if_needed(:outputs).each_slice(3) {|(_, id), (_, name), (_, enabled)|
|
71
|
+
return Output.new(self, id) if matches == id || matches == name
|
72
|
+
}
|
73
|
+
|
74
|
+
nil
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end; end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
#--
|
2
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
3
|
+
# Version 2, December 2004
|
4
|
+
#
|
5
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
6
|
+
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
7
|
+
#
|
8
|
+
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
9
|
+
#++
|
10
|
+
|
11
|
+
require 'weakref'
|
12
|
+
|
13
|
+
module MPD; class Controller
|
14
|
+
|
15
|
+
class Channels
|
16
|
+
class Channel
|
17
|
+
attr_reader :name
|
18
|
+
|
19
|
+
def initialize (channels, name)
|
20
|
+
@channels = channels
|
21
|
+
@name = name
|
22
|
+
@buffer = []
|
23
|
+
end
|
24
|
+
|
25
|
+
def incoming (text)
|
26
|
+
@buffer << text
|
27
|
+
end
|
28
|
+
|
29
|
+
def send_message (text)
|
30
|
+
@channels.send_message(@name, text)
|
31
|
+
end
|
32
|
+
|
33
|
+
def read_message
|
34
|
+
@channels.send :read_messages, true while @buffer.empty?
|
35
|
+
@buffer.shift
|
36
|
+
end
|
37
|
+
|
38
|
+
def read_message_nonblock
|
39
|
+
@channels.send :read_messages
|
40
|
+
@buffer.shift
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
include Enumerable
|
45
|
+
|
46
|
+
attr_reader :controller
|
47
|
+
|
48
|
+
def initialize (controller)
|
49
|
+
@controller = controller
|
50
|
+
@channels = []
|
51
|
+
end
|
52
|
+
|
53
|
+
def names
|
54
|
+
controller.do_and_raise_if_needed(:channels).map(&:last)
|
55
|
+
end
|
56
|
+
|
57
|
+
def each_name (&block)
|
58
|
+
names.each(&block)
|
59
|
+
end
|
60
|
+
|
61
|
+
def each
|
62
|
+
return to_enum unless block_given?
|
63
|
+
|
64
|
+
each_name {|name|
|
65
|
+
yield self[name]
|
66
|
+
}
|
67
|
+
|
68
|
+
self
|
69
|
+
end
|
70
|
+
|
71
|
+
def [] (name, sub = true)
|
72
|
+
subscribe name if sub
|
73
|
+
|
74
|
+
Channel.new(self, name).tap {|channel|
|
75
|
+
@channels << WeakRef.new(channel)
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
def subscribe (name)
|
80
|
+
controller.do_and_raise_if_needed :subscribe, name
|
81
|
+
end
|
82
|
+
|
83
|
+
def unsubscribe (name)
|
84
|
+
controller.do_and_raise_if_needed :unsubscribe, name
|
85
|
+
end
|
86
|
+
|
87
|
+
def send_message (name, text)
|
88
|
+
controller.do_and_raise_if_needed :sendmessage, name, text
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
def read_messages (wait = false)
|
93
|
+
response = controller.do_and_raise_if_needed :readmessages
|
94
|
+
|
95
|
+
if response.empty?
|
96
|
+
if wait
|
97
|
+
controller.wait_for :message
|
98
|
+
else
|
99
|
+
return false
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
response.each_slice(2) {|(_, name), (_, message)|
|
104
|
+
@channels.each {|channel|
|
105
|
+
next unless channel.weakref_alive? && channel.name.to_s == name.to_s
|
106
|
+
|
107
|
+
channel.incoming(message)
|
108
|
+
}
|
109
|
+
}
|
110
|
+
|
111
|
+
@channels.select!(&:weakref_alive?)
|
112
|
+
|
113
|
+
true
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
end; end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
#--
|
2
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
3
|
+
# Version 2, December 2004
|
4
|
+
#
|
5
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
6
|
+
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
7
|
+
#
|
8
|
+
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
9
|
+
#++
|
10
|
+
|
11
|
+
module MPD; class Controller
|
12
|
+
|
13
|
+
class Commands
|
14
|
+
class Command
|
15
|
+
attr_reader :name
|
16
|
+
|
17
|
+
def initialize (name, usable = true)
|
18
|
+
@name = name
|
19
|
+
@usable = usable
|
20
|
+
end
|
21
|
+
|
22
|
+
def usable?
|
23
|
+
@usable
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
include Enumerable
|
28
|
+
|
29
|
+
attr_reader :controller
|
30
|
+
|
31
|
+
def initialize (controller)
|
32
|
+
@controller = controller
|
33
|
+
@commands = []
|
34
|
+
|
35
|
+
controller.do_and_raise_if_needed(:commands).each {|_, name|
|
36
|
+
@commands << Command.new(name)
|
37
|
+
}
|
38
|
+
|
39
|
+
controller.do_and_raise_if_needed(:notcommands).each {|_, name|
|
40
|
+
@commands << Command.new(name, false)
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
def each (&block)
|
45
|
+
return to_enum unless block_given?
|
46
|
+
|
47
|
+
@commands.each(&block)
|
48
|
+
|
49
|
+
self
|
50
|
+
end
|
51
|
+
|
52
|
+
def inspect
|
53
|
+
"#<#{self.class.name}: #{map {|c| "#{'-' unless c.usable?}#{c.name}"}.join ' '}>"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end; end
|