From: Christophe Rhodes Date: Mon, 23 Apr 2012 20:19:36 +0000 (+0100) Subject: first cut of iPlayer interface for Emacs X-Git-Tag: v0.1~22 X-Git-Url: http://christophe.rhodes.io/gitweb/?a=commitdiff_plain;h=4477c6b3556d932de04a34aa3430523355460fdc;p=iplayer-el.git first cut of iPlayer interface for Emacs Very raw. Uses get_iplayer (spelled "get-iplayer" on my system); "parses" the tree output; uses Orgstruct for presentation, and text properties for user interface. Uncached calls to get-iplayer (the first in any four hours, unless the user has a cron job) blocks the whole of emacs for a good little while as get-iplayer spends its time pulling down metadata, and there is almost no useful feedback when downloading. On the other hand, it's easier for me than the command-line interface. --- 4477c6b3556d932de04a34aa3430523355460fdc diff --git a/iplayer.el b/iplayer.el new file mode 100644 index 0000000..47ec879 --- /dev/null +++ b/iplayer.el @@ -0,0 +1,107 @@ +(defun get-iplayer-tree (&rest args) + (with-temp-buffer + (apply #'call-process "get-iplayer" nil t nil "--nocopyright" "--type" "radio,tv" "--tree" "--terse" args) + (goto-char (point-min)) + (let (result program episodes) + (while (< (point) (point-max)) + (cond + ((looking-at "^\\w") + (when (and program episodes) + (push (cons program (reverse episodes)) result)) + (setf program (buffer-substring (point) (progn (end-of-line) (point)))) + (when (string-match "^\\(tv\\|radio\\), " program) + (setq program (substring program (match-end 0)))) + (setf episodes nil) + (unless (= (point) (point-max)) + (forward-char))) + ((looking-at "^ \\([0-9]+\\):\\s-\\(.*\\)$") + (let ((episode + (cons (buffer-substring (match-beginning 1) (match-end 1)) + (buffer-substring (match-beginning 2) (match-end 2))))) + (when (string-match "^\\(tv\\|radio\\), " (cdr episode)) + (rplacd episode (substring (cdr episode) (match-end 0)))) + (push episode episodes)) + (forward-line)) + (t (forward-line)))) + (reverse result)))) + +(defun display-iplayer-tree (tree) + (with-current-buffer (get-buffer-create "*iplayer*") + (delete-region (point-min) (point-max)) + (iplayer-mode) + (orgstruct-mode 1) + (dolist (entry tree) + (let ((program (car entry)) + (episodes (cdr entry))) + (insert (propertize (format "* %s\n" program) 'face 'outline-1)) + (dolist (episode episodes) + (insert (propertize (format "** %s\n" (cdr episode)) + 'face 'outline-2 'iplayer-id (car episode)))))) + (org-overview) + (goto-char (point-min))) + (switch-to-buffer (get-buffer-create "*iplayer*"))) + +(defvar iplayer-presets + '(("1" . "BBC One") + ("2" . "BBC Two") + ("3" . "BBC Three") + ("4" . "BBC Four") + + ("!" . "BBC Radio 1") + ("\"" . "BBC Radio 2") + ("£" . "BBC Radio 3") + ("$" . "BBC Radio 4") + ("%" . "BBC Radio 5 live") + ("^" . "BBC 6 Music") + ("&" . "BBC 7") + ("*" . "BBC Radio 4 Extra"))) + +(defun iplayer-preset (&optional prefix) + (interactive "p") + (let ((keys (this-command-keys)) + (presets (mapcar (lambda (x) (cons (read-kbd-macro (car x)) (cdr x))) iplayer-presets))) + (message "%s" (this-command-keys)) + (cond + ((= (length keys) 1) + (let ((channel (cdr (assoc keys presets)))) + (if channel + (iplayer-channel (format "^%s$" channel)) + (error "no preset for key %s" keys))))))) + +(defun iplayer-channel (channel) + (display-iplayer-tree (get-iplayer-tree "--channel" channel))) + +(defun iplayer-download () + (interactive) + (let ((id (get-text-property (point) 'iplayer-id))) + (if id + (let ((default-directory "~/iPlayer/")) + ;; should probably use a process filter instead to give us a + ;; progress bar + (message "downloading id %s" id) + (start-process "get-iplayer" " *get-iplayer*" "get-iplayer" "--get" (format "%s" id))) + (message "no id at point")))) + +(defconst iplayer-mode-map + (let ((map (make-sparse-keymap))) + (define-key map (kbd "0") 'iplayer) + (let ((presets "123456789!\"£$%^&*()")) + (dotimes (i (length presets)) + (define-key map (read-kbd-macro (substring presets i (1+ i))) + 'iplayer-preset))) + (define-key map (kbd "RET") 'iplayer-download) + map + )) + +(defun iplayer-mode () + "A major mode for the BBC's iPlayer. +\\{iplayer-mode-map}" + (interactive) + (use-local-map iplayer-mode-map) + (setq major-mode 'iplayer-mode mode-name "iPlayer")) + +(defun iplayer () + (interactive) + (display-iplayer-tree (get-iplayer-tree))) + +(provide 'iplayer)