ced7e170166906f773fbe74609dc1cd2a4968b2d
[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
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
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
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.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
154 socket.write("currentsong\n")
155 var rep = socket.read
156 for l in rep.split_with("\n") do
157 var words = l.split_with(" ")
158 if words.length > 1 then
159 var key = words[0].to_lower
160 var first_space = l.index_of(' ')
161 var rest = l.substring_from(first_space+1)
162 if key == "album:" then
163 album = rest
164 else if key == "artist:" then
165 artist = rest
166 else if key == "title:" then
167 title = rest
168 end
169 end
170 end
171
172 if album != null and artist != null and title != null then
173 return new SongInfo(album, artist, title)
174 else
175 return null
176 end
177 end
178 end
179
180 # MPD song info
181 class SongInfo
182 var album: String
183 var artist: String
184 var title: String
185 end
186
187 # MPD server status
188 class ServerStatus
189 var volume: nullable Int
190
191 var state: String
192 fun playing: Bool do return state == "play"
193 fun stopped: Bool do return state == "stop"
194
195 var elapsed: nullable Int
196 var time_at: nullable Int
197 var time_total: nullable Int
198 fun time_ratio: nullable Float do
199 if time_at == null or time_total == null then return null
200 return time_at.to_f / time_total.to_f
201 end
202
203 redef fun to_s do
204 var vol = "unknown"
205 if volume != null then vol = volume.to_s
206
207 var time_at = time_at
208 var time_total = time_total
209 var elapsed = elapsed
210 if time_at != null and time_total != null and elapsed != null then
211 return "volume: {vol}\nstate: {state}\nelapsed: {elapsed}\ntime_[at|total]: {time_at}:{time_total}\ntime_ratio: {time_ratio.as(not null)}"
212 else
213 return "volume: {vol}\nstate: {state}"
214 end
215 end
216 end