ruby-mpd 0.3.1 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +13 -14
- data/Gemfile +6 -0
- data/LICENSE.txt +339 -0
- data/README.md +276 -0
- data/Rakefile +1 -1
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/lib/ruby-mpd.rb +58 -50
- data/lib/ruby-mpd/parser.rb +4 -4
- data/lib/ruby-mpd/playlist.rb +3 -3
- data/lib/ruby-mpd/plugins/database.rb +8 -3
- data/lib/ruby-mpd/plugins/information.rb +1 -1
- data/lib/ruby-mpd/plugins/outputs.rb +7 -0
- data/lib/ruby-mpd/plugins/queue.rb +1 -1
- data/lib/ruby-mpd/song.rb +10 -1
- data/lib/ruby-mpd/version.rb +2 -2
- data/ruby-mpd.gemspec +23 -17
- metadata +69 -10
- data/README.rdoc +0 -296
- data/test/test_parser.rb +0 -52
data/Rakefile
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
1
2
|
require 'rake/testtask'
|
2
3
|
|
3
4
|
Rake::TestTask.new(:test) do |test|
|
@@ -6,7 +7,6 @@ end
|
|
6
7
|
|
7
8
|
desc "Open an irb session preloaded with this API"
|
8
9
|
task :console do
|
9
|
-
$:.unshift(File.expand_path('../lib', __FILE__))
|
10
10
|
require 'ruby-mpd'
|
11
11
|
require 'irb'
|
12
12
|
ARGV.clear
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "ruby/mpd"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/lib/ruby-mpd.rb
CHANGED
@@ -54,20 +54,13 @@ class MPD
|
|
54
54
|
@hostname = hostname
|
55
55
|
@port = port
|
56
56
|
@options = {callbacks: false}.merge(opts)
|
57
|
+
@password = opts.delete(:password) || nil
|
57
58
|
reset_vars
|
58
59
|
|
59
60
|
@mutex = Mutex.new
|
60
61
|
@callbacks = {}
|
61
62
|
end
|
62
63
|
|
63
|
-
# Initialize instance variables on new object, or on disconnect.
|
64
|
-
def reset_vars
|
65
|
-
@socket = nil
|
66
|
-
@version = nil
|
67
|
-
@tags = nil
|
68
|
-
end
|
69
|
-
private :reset_vars
|
70
|
-
|
71
64
|
# This will register a block callback that will trigger whenever
|
72
65
|
# that specific event happens.
|
73
66
|
#
|
@@ -93,44 +86,9 @@ class MPD
|
|
93
86
|
# @return [void]
|
94
87
|
def emit(event, *args)
|
95
88
|
return unless @callbacks[event]
|
96
|
-
@callbacks[event].each { |handle| handle.call
|
89
|
+
@callbacks[event].each { |handle| handle.call(*args) }
|
97
90
|
end
|
98
91
|
|
99
|
-
# Constructs a callback loop thread and/or resumes it.
|
100
|
-
# @return [Thread]
|
101
|
-
def callback_thread
|
102
|
-
@cb_thread ||= Thread.new(self) do |mpd|
|
103
|
-
old_status = {}
|
104
|
-
while true
|
105
|
-
status = mpd.status rescue {}
|
106
|
-
|
107
|
-
status[:connection] = mpd.connected?
|
108
|
-
|
109
|
-
status[:time] ||= [nil, nil] # elapsed, total
|
110
|
-
status[:audio] ||= [nil, nil, nil] # samp, bits, chans
|
111
|
-
status[:song] = mpd.current_song
|
112
|
-
|
113
|
-
status.each do |key, val|
|
114
|
-
next if val == old_status[key] # skip unchanged keys
|
115
|
-
emit key, *val # splat arrays
|
116
|
-
end
|
117
|
-
|
118
|
-
old_status = status
|
119
|
-
sleep 0.1
|
120
|
-
|
121
|
-
unless status[:connection] || Thread.current[:stop]
|
122
|
-
sleep 2
|
123
|
-
mpd.connect rescue nil
|
124
|
-
end
|
125
|
-
|
126
|
-
Thread.stop if Thread.current[:stop]
|
127
|
-
end
|
128
|
-
end
|
129
|
-
@cb_thread[:stop] = false
|
130
|
-
@cb_thread.run if @cb_thread.stop?
|
131
|
-
end
|
132
|
-
private :callback_thread
|
133
|
-
|
134
92
|
# Connect to the daemon.
|
135
93
|
#
|
136
94
|
# When called without any arguments, this will just connect to the server
|
@@ -139,16 +97,16 @@ class MPD
|
|
139
97
|
# @return [true] Successfully connected.
|
140
98
|
# @raise [MPDError] If connect is called on an already connected instance.
|
141
99
|
def connect(callbacks = nil)
|
142
|
-
raise ConnectionError, 'Already connected!' if
|
143
|
-
|
144
|
-
@socket = File.exists?(@hostname) ? UNIXSocket.new(@hostname) : TCPSocket.new(@hostname, @port)
|
100
|
+
raise ConnectionError, 'Already connected!' if connected?
|
145
101
|
|
146
102
|
# by protocol, we need to get a 'OK MPD <version>' reply
|
147
103
|
# should we fail to do so, the connection was unsuccessful
|
148
|
-
unless response =
|
104
|
+
unless response = socket.gets
|
149
105
|
reset_vars
|
150
106
|
raise ConnectionError, 'Unable to connect (possibly too many connections open)'
|
151
107
|
end
|
108
|
+
|
109
|
+
authenticate
|
152
110
|
@version = response.chomp.gsub('OK MPD ', '') # Read the version
|
153
111
|
|
154
112
|
if callbacks
|
@@ -208,6 +166,10 @@ class MPD
|
|
208
166
|
send_command :password, pass
|
209
167
|
end
|
210
168
|
|
169
|
+
def authenticate
|
170
|
+
send_command(:password, @password) if @password
|
171
|
+
end
|
172
|
+
|
211
173
|
# Ping the server.
|
212
174
|
# @macro returnraise
|
213
175
|
def ping
|
@@ -224,7 +186,7 @@ class MPD
|
|
224
186
|
# @return (see #handle_server_response)
|
225
187
|
# @raise [MPDError] if the command failed.
|
226
188
|
def send_command(command, *args)
|
227
|
-
raise ConnectionError, "Not connected to the server!"
|
189
|
+
raise ConnectionError, "Not connected to the server!" unless @socket
|
228
190
|
|
229
191
|
@mutex.synchronize do
|
230
192
|
begin
|
@@ -238,7 +200,49 @@ class MPD
|
|
238
200
|
end
|
239
201
|
end
|
240
202
|
|
241
|
-
|
203
|
+
private
|
204
|
+
|
205
|
+
# Initialize instance variables on new object, or on disconnect.
|
206
|
+
def reset_vars
|
207
|
+
@socket = nil
|
208
|
+
@version = nil
|
209
|
+
@tags = nil
|
210
|
+
end
|
211
|
+
|
212
|
+
# Constructs a callback loop thread and/or resumes it.
|
213
|
+
# @return [Thread]
|
214
|
+
def callback_thread
|
215
|
+
@cb_thread ||= Thread.new(self) do |mpd|
|
216
|
+
old_status = {}
|
217
|
+
while true
|
218
|
+
status = mpd.status rescue {}
|
219
|
+
|
220
|
+
status[:connection] = mpd.connected?
|
221
|
+
|
222
|
+
status[:time] ||= [nil, nil] # elapsed, total
|
223
|
+
status[:audio] ||= [nil, nil, nil] # samp, bits, chans
|
224
|
+
status[:song] = mpd.current_song rescue nil
|
225
|
+
status[:updating_db] ||= nil
|
226
|
+
|
227
|
+
status.each do |key, val|
|
228
|
+
next if val == old_status[key] # skip unchanged keys
|
229
|
+
emit key, *val # splat arrays
|
230
|
+
end
|
231
|
+
|
232
|
+
old_status = status
|
233
|
+
sleep 0.1
|
234
|
+
|
235
|
+
unless status[:connection] || Thread.current[:stop]
|
236
|
+
sleep 2
|
237
|
+
mpd.connect rescue nil
|
238
|
+
end
|
239
|
+
|
240
|
+
Thread.stop if Thread.current[:stop]
|
241
|
+
end
|
242
|
+
end
|
243
|
+
@cb_thread[:stop] = false
|
244
|
+
@cb_thread.run if @cb_thread.stop?
|
245
|
+
end
|
242
246
|
|
243
247
|
# Handles the server's response (called inside {#send_command}).
|
244
248
|
# Repeatedly reads the server's response from the socket.
|
@@ -265,6 +269,10 @@ class MPD
|
|
265
269
|
raise SERVER_ERRORS[err[:code].to_i], "[#{err[:command]}] #{err[:message]}"
|
266
270
|
end
|
267
271
|
|
272
|
+
def socket
|
273
|
+
@socket ||= File.exists?(@hostname) ? UNIXSocket.new(@hostname) : TCPSocket.new(@hostname, @port)
|
274
|
+
end
|
275
|
+
|
268
276
|
SERVER_ERRORS = {
|
269
277
|
1 => NotListError,
|
270
278
|
2 => ServerArgumentError,
|
data/lib/ruby-mpd/parser.rb
CHANGED
@@ -94,7 +94,7 @@ class MPD
|
|
94
94
|
def build_hash(string)
|
95
95
|
return {} if string.nil?
|
96
96
|
|
97
|
-
string.
|
97
|
+
string.lines.each_with_object({}) do |line, hash|
|
98
98
|
key, object = parse_line(line)
|
99
99
|
|
100
100
|
# if val appears more than once, make an array of vals.
|
@@ -109,19 +109,19 @@ class MPD
|
|
109
109
|
# Converts the response to MPD::Song objects.
|
110
110
|
# @return [Array<MPD::Song>] An array of songs.
|
111
111
|
def build_songs_list(array)
|
112
|
-
return array.map { |hash| Song.new(hash) }
|
112
|
+
return array.map { |hash| Song.new(self, hash) }
|
113
113
|
end
|
114
114
|
|
115
115
|
# Remove lines which we don't want.
|
116
116
|
def filter_lines(string, filter)
|
117
|
-
string.
|
117
|
+
string.lines.reject {|line| line =~ /(#{filter.join('|')}):/}.join
|
118
118
|
end
|
119
119
|
|
120
120
|
# Make chunks from string.
|
121
121
|
# @return [Array<String>]
|
122
122
|
def make_chunks(string)
|
123
123
|
first_key = string.match(/\A(.+?):\s?/)[1]
|
124
|
-
|
124
|
+
string.split(/\n(?=#{first_key})/).map(&:strip)
|
125
125
|
end
|
126
126
|
|
127
127
|
# Parses the response, determining per-command on what parsing logic
|
data/lib/ruby-mpd/playlist.rb
CHANGED
@@ -26,9 +26,9 @@ class MPD
|
|
26
26
|
def songs
|
27
27
|
result = @mpd.send_command(:listplaylistinfo, @name)
|
28
28
|
if result.to_s =~ URI::regexp
|
29
|
-
Song.new({:file => result, :time => 0})
|
29
|
+
Song.new(@mpd, {:file => result, :time => 0})
|
30
30
|
else
|
31
|
-
result.map {|hash| Song.new(hash) }
|
31
|
+
result.map {|hash| Song.new(@mpd, hash) }
|
32
32
|
end
|
33
33
|
rescue TypeError
|
34
34
|
puts "Files inside Playlist '#{@name}' do not exist!"
|
@@ -78,7 +78,7 @@ class MPD
|
|
78
78
|
# Moves song with SONGID in the playlist to the position SONGPOS.
|
79
79
|
# @macro returnraise
|
80
80
|
def move(songid, songpos)
|
81
|
-
@mpd.send_command :playlistmove, @name
|
81
|
+
@mpd.send_command :playlistmove, @name, songid, songpos
|
82
82
|
end
|
83
83
|
|
84
84
|
# Renames the playlist to +new_name+.
|
@@ -91,8 +91,13 @@ class MPD
|
|
91
91
|
else
|
92
92
|
command = options[:strict] ? :find : :search
|
93
93
|
end
|
94
|
-
|
95
|
-
|
94
|
+
|
95
|
+
response = send_command(command, params)
|
96
|
+
if response == true
|
97
|
+
return true
|
98
|
+
else
|
99
|
+
build_songs_list response
|
100
|
+
end
|
96
101
|
end
|
97
102
|
|
98
103
|
# Tell the server to update the database. Optionally,
|
@@ -117,7 +122,7 @@ class MPD
|
|
117
122
|
#
|
118
123
|
# @return [Array<String>] Array of directory names
|
119
124
|
def directories(path = nil)
|
120
|
-
return files[:directory]
|
125
|
+
return files(path)[:directory]
|
121
126
|
end
|
122
127
|
|
123
128
|
# Lists all of the albums in the database.
|
@@ -18,7 +18,7 @@ class MPD
|
|
18
18
|
def current_song
|
19
19
|
hash = send_command :currentsong
|
20
20
|
# if there is no current song (we get true, then return nil)
|
21
|
-
hash.is_a?(TrueClass) ? nil : Song.new(hash)
|
21
|
+
hash.is_a?(TrueClass) ? nil : Song.new(self, hash)
|
22
22
|
end
|
23
23
|
|
24
24
|
# Waits until there is a noteworthy change in one or more of MPD's subsystems.
|
@@ -21,6 +21,13 @@ class MPD
|
|
21
21
|
def disableoutput(num)
|
22
22
|
send_command :disableoutput, num
|
23
23
|
end
|
24
|
+
|
25
|
+
# Toggles specified output.
|
26
|
+
# @param [Integer] num Number of the output to enable.
|
27
|
+
# @macro returnraise
|
28
|
+
def toggleoutput(num)
|
29
|
+
send_command :toggleoutput, num
|
30
|
+
end
|
24
31
|
end
|
25
32
|
end
|
26
33
|
end
|
@@ -78,7 +78,7 @@ class MPD
|
|
78
78
|
# Returns the song with the +songid+ in the playlist,
|
79
79
|
# @return [MPD::Song]
|
80
80
|
def song_with_id(songid)
|
81
|
-
Song.new send_command(:playlistid, songid)
|
81
|
+
Song.new(self, send_command(:playlistid, songid))
|
82
82
|
end
|
83
83
|
|
84
84
|
# Searches for songs in the queue matched by the what
|
data/lib/ruby-mpd/song.rb
CHANGED
@@ -7,7 +7,8 @@ class MPD::Song
|
|
7
7
|
# length in seconds
|
8
8
|
attr_reader :file, :title, :time, :artist, :album, :albumartist
|
9
9
|
|
10
|
-
def initialize(options)
|
10
|
+
def initialize(mpd, options)
|
11
|
+
@mpd = mpd
|
11
12
|
@data = {} # allowed fields are @types + :file
|
12
13
|
@time = options.delete(:time) { [nil] }.first # HAXX for array return
|
13
14
|
@file = options.delete(:file)
|
@@ -28,6 +29,14 @@ class MPD::Song
|
|
28
29
|
return '--:--' if @time.nil?
|
29
30
|
"#{@time / 60}:#{"%02d" % (@time % 60)}"
|
30
31
|
end
|
32
|
+
|
33
|
+
# Retrieve "comments" metadata from a file and cache it in the object.
|
34
|
+
#
|
35
|
+
# @return [Hash] Key value pairs from "comments" metadata on a file.
|
36
|
+
# @return [Boolean] True if comments are empty
|
37
|
+
def comments
|
38
|
+
@comments ||= @mpd.send_command :readcomments, @file
|
39
|
+
end
|
31
40
|
|
32
41
|
# Pass any unknown calls over to the data hash.
|
33
42
|
def method_missing(m, *a)
|
data/lib/ruby-mpd/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
class MPD
|
2
|
-
VERSION = '0.3.
|
3
|
-
end
|
2
|
+
VERSION = '0.3.2'
|
3
|
+
end
|
data/ruby-mpd.gemspec
CHANGED
@@ -1,20 +1,26 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'ruby-mpd/version'
|
4
5
|
|
5
|
-
Gem::Specification.new do |
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
s.license = 'GPL-2'
|
11
|
-
s.authors = ["Blaž Hrastnik"]
|
12
|
-
s.email = ['speed.the.bboy@gmail.com']
|
13
|
-
s.summary = "Modern client library for MPD"
|
14
|
-
s.description = "A powerful, modern and feature complete library for the Music Player Daemon."
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "ruby-mpd"
|
8
|
+
spec.version = MPD::VERSION
|
9
|
+
spec.authors = ["Blaž Hrastnik"]
|
10
|
+
spec.email = ["speed.the.bboy@gmail.com"]
|
15
11
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
12
|
+
spec.summary = "Modern client library for MPD"
|
13
|
+
spec.description = "A powerful, modern and feature complete library for the Music Player Daemon"
|
14
|
+
spec.homepage = "https://github.com/archSeer/ruby-mpd"
|
15
|
+
spec.license = "GPL-2"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.9"
|
23
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
24
|
+
spec.add_development_dependency "minitest", "~> 5.5"
|
25
|
+
spec.add_development_dependency "rspec", "~> 3.1"
|
20
26
|
end
|
metadata
CHANGED
@@ -1,17 +1,73 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-mpd
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Blaž Hrastnik
|
8
8
|
autorequire:
|
9
|
-
bindir:
|
9
|
+
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
12
|
-
dependencies:
|
11
|
+
date: 2015-05-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.9'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.9'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '5.5'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '5.5'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.1'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.1'
|
13
69
|
description: A powerful, modern and feature complete library for the Music Player
|
14
|
-
Daemon
|
70
|
+
Daemon
|
15
71
|
email:
|
16
72
|
- speed.the.bboy@gmail.com
|
17
73
|
executables: []
|
@@ -20,8 +76,12 @@ extra_rdoc_files: []
|
|
20
76
|
files:
|
21
77
|
- ".gitignore"
|
22
78
|
- COPYING
|
23
|
-
-
|
79
|
+
- Gemfile
|
80
|
+
- LICENSE.txt
|
81
|
+
- README.md
|
24
82
|
- Rakefile
|
83
|
+
- bin/console
|
84
|
+
- bin/setup
|
25
85
|
- lib/ruby-mpd.rb
|
26
86
|
- lib/ruby-mpd/exceptions.rb
|
27
87
|
- lib/ruby-mpd/parser.rb
|
@@ -39,7 +99,6 @@ files:
|
|
39
99
|
- lib/ruby-mpd/song.rb
|
40
100
|
- lib/ruby-mpd/version.rb
|
41
101
|
- ruby-mpd.gemspec
|
42
|
-
- test/test_parser.rb
|
43
102
|
homepage: https://github.com/archSeer/ruby-mpd
|
44
103
|
licenses:
|
45
104
|
- GPL-2
|
@@ -60,9 +119,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
60
119
|
version: '0'
|
61
120
|
requirements: []
|
62
121
|
rubyforge_project:
|
63
|
-
rubygems_version: 2.2.
|
122
|
+
rubygems_version: 2.2.2
|
64
123
|
signing_key:
|
65
124
|
specification_version: 4
|
66
125
|
summary: Modern client library for MPD
|
67
|
-
test_files:
|
68
|
-
|
126
|
+
test_files: []
|
127
|
+
has_rdoc:
|