misc/vim: inform the user when no results are found
[nit.git] / lib / mpd.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2013 Alexis Laferrière <alexis.laf@xymus.net>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Client for a MPD server
18 module mpd
19
20 import socket
21
22 # Connection to a MPD server
23 class MPDConnection
24
25 # Socket connection to server.
26 var socket: nullable TCPStream = null
27
28 # Server hostname.
29 var host: String
30
31 # Server port.
32 var port: Int
33
34 # Server password.
35 var password: nullable String
36
37 # Last occured error.
38 var error: nullable String = null
39
40 # Connect to the MPD server
41 fun connect
42 do
43 var p: nullable TCPStream = null
44
45 p = new TCPStream.connect(host, port)
46 assert p.connected
47
48 sys.nanosleep(0,5000)
49
50 var rep = p.read(1024)
51 assert not rep.is_empty
52 if not rep.has_prefix("OK") then
53 print "MPD responded {rep}"
54 abort
55 end
56
57 socket = p
58
59 var password = password
60 if password != null then
61 write_and_check("password {password}\n")
62 end
63 end
64
65 # Write a command to the MPD server
66 protected fun write_and_check(msg: String)
67 do
68 if socket == null then connect
69
70 socket.write(msg)
71 sys.nanosleep(0,5000)
72 var rep = socket.read(1024)
73 if not rep.has_prefix("OK") then
74 print "Error: MPD responded {rep}"
75 socket.close
76 socket = null
77 end
78 end
79
80 # Get MPD server status
81 fun status: nullable ServerStatus
82 do
83 if socket == null then connect
84
85 var volume: nullable Int = null
86 var state: nullable String = null
87 var elapsed: nullable Int = null
88 var time_at: nullable Int = null
89 var time_total: nullable Int = null
90
91 # get current status
92 socket.write("status\n")
93 var rep = socket.read(1024)
94 for l in rep.split_with("\n") do
95 var words = l.split_with(" ")
96 if words.length > 1 then
97 var key = words[0].to_lower
98 var first_space = l.chars.index_of(' ')
99 var rest = l.substring_from(first_space+1)
100 if key == "volume:" then
101 volume = rest.to_i
102 if volume == -1 then volume = null
103 else if key == "state:" then
104 state = rest
105 else if key == "elapsed:" then
106 elapsed = rest.to_i
107 else if key == "time:" then
108 var times = rest.split(":")
109 time_at = times[0].to_i
110 time_total = times[1].to_i
111 end
112 end
113 end
114
115 if state != null then
116 var status = new ServerStatus(volume, state, elapsed, time_at, time_total)
117 return status
118 else
119 return null
120 end
121 end
122
123 # Set the volume relatively
124 fun relative_volume=(diff: Int)
125 do
126 if socket == null then connect
127
128 var status = status
129 if status != null then
130 var vol = status.volume
131 if vol != null then
132 var new_vol = vol + diff
133 new_vol = new_vol.max(0).min(100)
134 volume = new_vol
135 return
136 end
137 end
138
139 error = "Cannot get volume"
140 end
141
142 # Set MPD server volume.
143 fun volume=(vol: Int) do write_and_check("setvol {vol}\n")
144
145 # Pause music playing on the MPD server
146 fun pause do write_and_check("pause\n")
147
148 # Stop music playing on the MPD server
149 fun stop do write_and_check("stop\n")
150
151 # Play music playing on the MPD server
152 fun play do write_and_check("play\n")
153
154 # Get information on the currently playing song on the MPD server
155 fun current_song: nullable SongInfo
156 do
157 if socket == null then connect
158
159 var album: nullable String = null
160 var artist: nullable String = null
161 var title: nullable String = null
162 var time: nullable Int = null
163
164 socket.write("currentsong\n")
165 var rep = socket.read(1024)
166 for l in rep.split_with("\n") do
167 var words = l.split_with(" ")
168 if words.length > 1 then
169 var key = words[0].to_lower
170 var first_space = l.chars.index_of(' ')
171 var rest = l.substring_from(first_space+1)
172 if key == "album:" then
173 album = rest
174 else if key == "artist:" then
175 artist = rest
176 else if key == "title:" then
177 title = rest
178 else if key == "time:" then
179 time = rest.to_i
180 end
181 end
182 end
183
184 if album != null and artist != null and
185 title != null and time != null then
186 return new SongInfo(album, artist, title, time)
187 else
188 return null
189 end
190 end
191
192 # Load playlist named `name`.
193 fun load_playlist(name: String)
194 do
195 write_and_check "load \"{name}\""
196 end
197 end
198
199 # MPD song info
200 class SongInfo
201 # Song album.
202 var album: String
203
204 # Song artist.
205 var artist: String
206
207 # Song title.
208 var title: String
209
210 # Song total duration.
211 var time: Int
212 end
213
214 # MPD server status
215 class ServerStatus
216
217 # MPD server volume.
218 var volume: nullable Int
219
220 # Playback state (play/stop/pause).
221 var state: String
222
223 # Is MPD server playing?
224 fun playing: Bool do return state == "play"
225
226 # Is MPD server stopped?
227 fun stopped: Bool do return state == "stop"
228
229 # Time elapsed within the current song.
230 var elapsed: nullable Int
231
232 # TODO comment
233 var time_at: nullable Int
234
235 # Total time of the current song.
236 var time_total: nullable Int
237
238 # Get the cursor position on the song duration.
239 fun time_ratio: nullable Float do
240 if time_at == null or time_total == null then return null
241 return time_at.to_f / time_total.to_f
242 end
243
244 redef fun to_s do
245 var vol = "unknown"
246 if volume != null then vol = volume.to_s
247
248 var time_at = time_at
249 var time_total = time_total
250 var elapsed = elapsed
251 if time_at != null and time_total != null and elapsed != null then
252 return "volume: {vol}\nstate: {state}\nelapsed: {elapsed}\ntime_[at|total]: {time_at}:{time_total}\ntime_ratio: {time_ratio.as(not null)}"
253 else
254 return "volume: {vol}\nstate: {state}"
255 end
256 end
257 end