3 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4 ;; This program is free software; you can redistribute it and/or ;;
5 ;; modify it under the terms of the GNU General Public License as ;;
6 ;; published by the Free Software Foundation; either version 3, or (at ;;
7 ;; your option) any later version. ;;
9 ;; This program is distributed in the hope that it will be useful, but ;;
10 ;; WITHOUT ANY WARRANTY; without even the implied warranty of ;;
11 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;;
12 ;; General Public License for more details. ;;
14 ;; You should have received a copy of the GNU General Public License ;;
15 ;; along with this program. If not, see <http://www.gnu.org/licenses/>. ;;
17 ;; Written by and Copyright (C) Francois Fleuret ;;
18 ;; Contact <francois@fleuret.org> for comments & bug reports ;;
19 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
21 ;; Is it me, or the slave mode of mplayer is ugly to parse? Did I miss
24 (defcustom media/mplayer/args nil
25 "List of arguments for mplayer."
29 (defcustom media/mplayer/timing-request-period 0.25
30 "Period for the timing requests in second(s). Larger values
31 load Emacs less. Nil means no timing."
35 (defcustom media/mplayer/capture-dir nil
36 "States where to save the dumped streams."
40 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
42 ;; It is impossible to tell mplayer to send information every time dt
43 ;; or so, hence this mess with a timer to avoid overloading emacs with
44 ;; the processing of the information
46 (defvar media/mplayer/timer nil
47 "A timer to request the timing position.")
49 (defun media/mplayer/timing-request ()
50 (if media/mplayer/process
51 (unless media/mplayer/paused
52 (media/mplayer/write "get_time_pos\n")
54 (media/mplayer/stop-timing-requests)
57 (defun media/mplayer/start-timing-requests ()
58 (when media/mplayer/timing-request-period
59 (media/mplayer/stop-timing-requests)
60 (setq media/mplayer/timer
62 media/mplayer/timing-request-period
63 'media/mplayer/timing-request))
67 (defun media/mplayer/stop-timing-requests ()
68 (when media/mplayer/timer
69 (cancel-timer media/mplayer/timer)
70 (setq media/mplayer/timer nil)
73 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
75 (setq media/mplayer/protocol-regexp
76 "^\\(AUDIO:\\|Exiting...\\|Starting\\|ANS_LENGTH\\|ANS_TIME_POSITION\\|Cache fill:\\|ICY Info:\\) *\\(.*\\)$")
78 (defun media/mplayer/filter-subfunctions (cmd param)
79 ;; (unless (string= cmd "A:")
80 ;; (message "cmd=%s param=%s" cmd param)
89 ;; (message "ICY Info \"%s\"" param)
91 (if (string-match "StreamTitle='\\([^;]*\\)';" param)
93 (setq media/current-song-in-stream
94 (let ((s (match-string 1 param)))
95 (concat (if (string= s "")
97 (encode-coding-string s 'latin-1)
101 (format-time-string "%a %b %d %H:%M:%S")
106 ;; If we did not parse it properly, reset the
107 ;; song name, and display the ICY string raw
108 (setq media/current-song-in-stream nil)
109 (message "ICY Info \"%s\"" param)
112 (when media/mplayer/capture-dir
113 (let ((coding-system-for-write 'raw-text-unix))
116 (concat media/current-song-in-stream "\n"))
117 (write-region nil nil (concat media/mplayer/capture-dir "/log") t))))
119 (if (and media/current-song-in-stream media/current-information)
120 (media/show-current-information))
123 ;; ----------------------------------------
127 (setq media/song-duration
128 (string-to-number (substring param 1))))
130 ;; ----------------------------------------
134 (setq media/song-current-time
135 (string-to-number (substring param 1)))
137 (when (and media/duration-to-history
138 (< media/mplayer/cumulated-duration media/duration-to-history))
140 (when media/mplayer/last-current-time
141 (setq media/mplayer/cumulated-duration
142 (+ media/mplayer/cumulated-duration
143 (- media/song-current-time media/mplayer/last-current-time))))
145 (when (>= media/mplayer/cumulated-duration media/duration-to-history)
146 (media/put-in-history)
149 (setq media/mplayer/last-current-time media/song-current-time)
153 ;; ----------------------------------------
157 ;; param = "44100 Hz, 2 ch, s16le, 128.0 kbit/9.07% (ratio: 16000->176400)"
158 (when (string-match "^\\([0-9]+\\) Hz, \\([0-9]+\\) ch.* \\([0-9.]+\\) kbit"
160 (setq media/current-information
161 (list media/mplayer/url
162 (string-to-number (match-string 1 param))
163 (string-to-number (match-string 2 param))
164 (string-to-number (match-string 3 param))))
166 (run-hooks 'media/play-hook)
169 ;; ----------------------------------------
172 (media/mplayer/write "get_time_length\n")
173 (when media/mplayer/capture-dir
174 (media/mplayer/write "capturing\n")
175 ;; (message "Capturing stream in %s" media/mplayer/capture-dir)
179 ;; ----------------------------------------
182 (when (string-match "(\\([0-9]+\\) bytes" param)
183 (message "Caching stream (%dkb)"
184 (/ (string-to-number (match-string 1 param)) 1024))))
186 ;; ----------------------------------------
189 (setq media/mplayer/exit-type
190 (cdr (assoc param '(("(End of file)" . file-finished)
192 media/current-information nil
193 media/song-duration nil
194 media/song-current-time nil)
196 (when media/mplayer/process (kill-process media/mplayer/process))
198 (force-mode-line-update)
201 ;; ----------------------------------------
210 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
212 (defun media/mplayer/filter (process str)
213 (setq media/mplayer/buffer (concat media/mplayer/buffer str))
215 (while (and (< start (length media/mplayer/buffer))
216 (string-match "\\(.*\\)[\n
\r]+" media/mplayer/buffer start))
217 (setq start (1+ (match-end 1)))
218 (let ((line (match-string 1 media/mplayer/buffer)))
219 (when (string-match media/mplayer/protocol-regexp line)
220 (media/mplayer/filter-subfunctions (match-string 1 line) (match-string 2 line))))
222 (setq media/mplayer/buffer (substring media/mplayer/buffer start)))
225 (defun media/mplayer/sentinel (process str) ()
226 ;; (message "Media process got \"%s\"" (replace-regexp-in-string "\n" "" str))
227 (unless (eq (process-status media/mplayer/process) 'run)
228 (setq media/current-information nil
229 media/mplayer/process nil
230 media/song-current-time nil
231 media/song-duration nil)
233 (media/mplayer/stop-timing-requests)
235 (if (eq media/mplayer/exit-type 'file-finished)
236 (run-hooks 'media/finished-hook)
237 (run-hooks 'media/error-hook))
239 (force-mode-line-update))
242 (defun media/mplayer/write (&rest l)
243 ;; (message "****** WROTE \"%s\"" (replace-regexp-in-string "\n" "[RETURN]" (apply 'format l)))
244 (if media/mplayer/process (process-send-string media/mplayer/process (apply 'format l))
245 (error "No mplayer process"))
248 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
249 ;; Player control abstract layer ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
250 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
252 (defun media/api/init () "Called once when the media application starts"
253 (setq media/player-id "MPlayer"
254 media/mplayer/url nil
255 media/mplayer/buffer "" ;; Used as read buffer
256 media/mplayer/process nil
257 media/mplayer/exit-type nil
258 media/mplayer/paused nil
259 media/song-duration nil
260 media/song-current-time nil
261 media/mplayer/cumulated-duration 0
262 media/mplayer/last-current-time nil
266 (defun media/api/cleanup () "Called when killing the application's buffer"
267 (when media/mplayer/process
268 (delete-process media/mplayer/process)
269 (media/mplayer/stop-timing-requests)
270 (setq media/mplayer/process nil)))
272 (defun media/api/play (url) (interactive)
273 (setq media/mplayer/url url)
275 (when media/mplayer/process (kill-process media/mplayer/process))
277 ;; (if media/mplayer/process
278 ;; (media/mplayer/write (concat "loadfile "
279 ;; (replace-regexp-in-string "^file://" "" media/mplayer/url)
282 (setq media/mplayer/process
286 '("mplayer" nil "mplayer" "-slave" "-quiet")
288 (when (string-match "\\(asx\\|m3u\\|pls\\|ram\\)$" media/mplayer/url)
289 (if media/mplayer/capture-dir
291 (concat media/mplayer/capture-dir
293 (replace-regexp-in-string "[^a-zA-Z0-9\.]" "_" media/mplayer/url)
294 (format-time-string "-%Y-%m-%d-%H:%M:%S"))
300 (list (replace-regexp-in-string "^file://" "" media/mplayer/url)))
302 media/mplayer/exit-type 'unknown
303 media/mplayer/paused nil
304 media/song-duration nil
305 media/song-current-time nil
306 media/mplayer/cumulated-duration 0
307 media/mplayer/last-current-time nil
310 (set-process-filter media/mplayer/process 'media/mplayer/filter)
311 (set-process-sentinel media/mplayer/process 'media/mplayer/sentinel)
312 (process-kill-without-query media/mplayer/process)
313 (media/mplayer/start-timing-requests)
314 (media/mplayer/write "get_time_pos\n")
317 (defun media/api/stop () (interactive)
318 (media/mplayer/write "quit\n")
321 (defun media/api/pause () (interactive)
322 (media/mplayer/write "pause\n")
323 (setq media/mplayer/paused (not media/mplayer/paused))
326 (defun media/api/set-volume (mode value) (interactive)
327 (if (eq mode 'absolute)
328 (media/mplayer/write "volume %s 1\n" value)
330 (media/mplayer/write "volume +%s\n" value)
331 (media/mplayer/write "volume %s\n" value))))
333 (defun media/api/jump-at-percent (percent) (interactive)
334 (setq media/song-current-time nil)
335 (when (< media/mplayer/cumulated-duration media/duration-to-history)
336 (setq media/mplayer/cumulated-duration 0
337 media/mplayer/last-current-time nil))
338 (media/mplayer/write "seek %s 1\n" percent)
339 (media/mplayer/write "get_time_pos\n")
342 (defun media/api/jump-at-time (mode time) (interactive)
343 (setq media/song-current-time nil)
344 (when (< media/mplayer/cumulated-duration media/duration-to-history)
345 (setq media/mplayer/cumulated-duration 0
346 media/mplayer/last-current-time nil))
347 (if (eq mode 'absolute)
348 (media/mplayer/write "seek %s 2\n" time)
349 (media/mplayer/write "seek %s 0\n" time))
350 (media/mplayer/write "get_time_pos\n")
353 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;