mpd 0.17.0
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/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
|