cf9b67701f5c8c4528661a0622f84194fd496d15
[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 var socket: nullable Socket = null
25
26 var host: String
27 var port: Int
28 var password: nullable String
29
30 var error: nullable String = null
31
32 # Connect to the MPD server
33 fun connect
34 do
35 var p: nullable Socket = null
36
37 p = new Socket.stream_with_host(host, port)
38 p.connect
39
40 sys.nanosleep(0,5000)
41
42 var rep = p.read(1024)
43 assert not rep.is_empty
44 if not rep.has_prefix("OK") then
45 print "MPD responded {rep}"
46 abort
47 end
48
49 socket = p
50
51 var password = password
52 if password != null then
53 write_and_check("password {password}\n")
54 end
55 end
56
57 # Write a command to the MPD server
58 protected fun write_and_check(msg: String)
59 do
60 if socket == null then connect
61
62 socket.write(msg)
63 sys.nanosleep(0,5000)
64 var rep = socket.read(1024)
65 if not rep.has_prefix("OK") then
66 print "Error: MPD responded {rep}"
67 socket.close
68 socket = null
69 end
70 end
71
72 # Get MPD server status
73 fun status: nullable ServerStatus
74 do
75 if socket == null then connect
76
77 var volume: nullable Int = null
78 var state: nullable String = null
79 var elapsed: nullable Int = null
80 var time_at: nullable Int = null
81 var time_total: nullable Int = null
82
83 # get current status
84 socket.write("status\n")
85 var rep = socket.read(1024)
86 for l in rep.split_with("\n") do
87 var words = l.split_with(" ")
88 if words.length > 1 then
89 var key = words[0].to_lower
90 var first_space = l.chars.index_of(' ')
91 var rest = l.substring_from(first_space+1)
92 if key == "volume:" then
93 volume = rest.to_i
94 if volume == -1 then volume = null
95 else if key == "state:" then
96 state = rest
97 else if key == "elapsed:" then
98 elapsed = rest.to_i
99 else if key == "time:" then
100 var times = rest.split(":")
101 time_at = times[0].to_i
102 time_total = times[1].to_i
103 end
104 end
105 end
106
107 if state != null then
108 var status = new ServerStatus(volume, state, elapsed, time_at, time_total)
109 return status
110 else
111 return null
112 end
113 end
114
115 # Set the volume relatively
116 fun relative_volume=(diff: Int)
117 do
118 if socket == null then connect
119
120 var status = status
121 if status != null then
122 var vol = status.volume
123 if vol != null then
124 var new_vol = vol + diff
125 new_vol = new_vol.max(0).min(100)
126 volume = new_vol
127 return
128 end
129 end
130
131 error = "Cannot get volume"
132 end
133
134 fun volume=(vol: Int) do write_and_check("setvol {vol}\n")
135
136 # Pause music playing on the MPD server
137 fun pause do write_and_check("pause\n")
138
139 # Stop music playing on the MPD server
140 fun stop do write_and_check("stop\n")
141
142 # Play music playing on the MPD server
143 fun play do write_and_check("play\n")
144
145 # Get information on the currently playing song on the MPD server
146 fun current_song: nullable SongInfo
147 do
148 if socket == null then connect
149
150 var album: nullable String = null
151 var artist: nullable String = null
152 var title: nullable String = null
153 var time: nullable Int = null
154
155 socket.write("currentsong\n")
156 var rep = socket.read(1024)
157 for l in rep.split_with("\n") do
158 var words = l.split_with(" ")
159 if words.length > 1 then
160 var key = words[0].to_lower
161 var first_space = l.chars.index_of(' ')
162 var rest = l.substring_from(first_space+1)
163 if key == "album:" then
164 album = rest
165 else if key == "artist:" then
166 artist = rest
167 else if key == "title:" then
168 title = rest
169 else if key == "time:" then
170 time = rest.to_i
171 end
172 end
173 end
174
175 if album != null and artist != null and
176 title != null and time != null then
177 return new SongInfo(album, artist, title, time)
178 else
179 return null
180 end
181 end
182
183 fun load_playlist(name: String)
184 do
185 write_and_check "load \"{name}\""
186 end
187 end
188
189 # MPD song info
190 class SongInfo
191 var album: String
192 var artist: String
193 var title: String
194 var time: Int
195 end
196
197 # MPD server status
198 class ServerStatus
199 var volume: nullable Int
200
201 var state: String
202 fun playing: Bool do return state == "play"
203 fun stopped: Bool do return state == "stop"
204
205 var elapsed: nullable Int
206 var time_at: nullable Int
207 var time_total: nullable Int
208 fun time_ratio: nullable Float do
209 if time_at == null or time_total == null then return null
210 return time_at.to_f / time_total.to_f
211 end
212
213 redef fun to_s do
214 var vol = "unknown"
215 if volume != null then vol = volume.to_s
216
217 var time_at = time_at
218 var time_total = time_total
219 var elapsed = elapsed
220 if time_at != null and time_total != null and elapsed != null then
221 return "volume: {vol}\nstate: {state}\nelapsed: {elapsed}\ntime_[at|total]: {time_at}:{time_total}\ntime_ratio: {time_ratio.as(not null)}"
222 else
223 return "volume: {vol}\nstate: {state}"
224 end
225 end
226 end