Christophe Weblog Wiki Code Publications Music
implement process sentinel for iplayer downloads
authorChristophe Rhodes <csr21@cantab.net>
Thu, 22 May 2014 07:21:47 +0000 (08:21 +0100)
committerChristophe Rhodes <csr21@cantab.net>
Thu, 22 May 2014 07:21:47 +0000 (08:21 +0100)
process state, in principle, goes

  connecting -> downloading -> transcoding -> finished
     |               v             |
     '-----------> failed <--------'

thoughat the moment there's a wrong edge from downloading to finished
if the get-iplayer process is terminated at an unfortunate time.

iplayer.el

index 467d65609cb2861dc79fe73fcc5e5ffddb555952..ce773e5e02b24469a69608a25e80770e856d736e 100644 (file)
@@ -219,44 +219,60 @@ The presets are defined in the variable `iplayer-presets'."
       (iplayer-channel iplayer-current-channel)
     (iplayer-show-all)))
 
+(defun iplayer-download-display-state (process)
+  (let ((id (process-get process 'iplayer-id))
+        (state (process-get process 'iplayer-state))
+        (progress (process-get process 'iplayer-progress)))
+    (with-current-buffer (get-buffer-create "*iplayer-progress*")
+      (goto-char (point-min))
+      (let ((found (re-search-forward (format "^%s:" id) nil 'end)))
+        (unless found
+          (unless (= (point) (progn (forward-line 0) (point)))
+            (goto-char (point-max))
+            (newline)))
+        (forward-line 0)
+        (let ((beg (point)))
+          (end-of-line)
+          (delete-region beg (point)))
+        (insert (format "%s: %s %s%%" id state progress))))))
+
 (defun iplayer-download-process-filter (process string)
+  (catch 'no-progress
+    (cond
+     ((string-match "^Starting download" string)
+      (process-put process 'iplayer-state 'downloading)
+      (process-put process 'iplayer-progress 0.0))
+     ((and (eql (process-get process 'iplayer-state) 'downloading)
+           (string-match "(\\([0-9]\\{1,3\\}.[0-9]\\)%)$" string))
+      (process-put process 'iplayer-progress (string-to-number (match-string 1 string))))
+     ((string-match "Started writing to temp file" string)
+      (process-put process 'iplayer-state 'transcoding)
+      (process-put process 'iplayer-progress 0.0))
+     ((string-match " Progress: =*>?\\([0-9]\\{1,3\\}\\)%-*|" string)
+      (let ((idx (match-beginning 0)) (data (match-data)))
+        (while (string-match " Progress: =*>?\\([0-9]\\{1,3\\}\\)%-*|" string (match-end 0))
+          (setq idx (match-beginning 0))
+          (setq data (match-data)))
+        (set-match-data data)
+        (process-put process 'iplayer-progress (string-to-number (match-string 1 string)))))
+     (t (with-current-buffer (process-buffer process)
+          (insert string))
+        (throw 'no-progress nil)))
+    (iplayer-download-display-state process)))
+
+(defun iplayer-download-process-sentinel (process string)
   (cond
-   ((string-match "^Starting download" string)
-    (process-put process 'iplayer-state 'downloading)
-    (process-put process 'iplayer-progress 0.0))
-   ((and (eql (process-get process 'iplayer-state) 'downloading)
-         (string-match "(\\([0-9]\\{1,3\\}.[0-9]\\)%)$" string))
-    (process-put process 'iplayer-progress (string-to-number (match-string 1 string))))
-   ((string-match "Started writing to temp file" string)
-    (process-put process 'iplayer-state 'transcoding)
-    (process-put process 'iplayer-progress 0.0))
-   ((string-match " Progress: =*>?\\([0-9]\\{1,3\\}\\)%-*|" string)
-    (let ((idx (match-beginning 0)) (data (match-data)))
-      (while (string-match " Progress: =*>?\\([0-9]\\{1,3\\}\\)%-*|" string (match-end 0))
-        (setq idx (match-beginning 0))
-        (setq data (match-data)))
-      (set-match-data data)
-      (process-put process 'iplayer-progress (string-to-number (match-string 1 string)))))
-   (t (with-current-buffer (process-buffer process)
-        (newline)
-        (insert string)
-        (newline))))
-  (with-current-buffer (get-buffer-create "*iplayer-progress*")
-    (goto-char (point-min))
-    (let ((found
-           (re-search-forward (format "^%s:" (process-get process 'iplayer-id)) nil 'end)))
-      (unless found
-        (unless (= (point) (progn (forward-line 0) (point)))
-          (goto-char (point-max))
-          (newline)))
-      (forward-line 0)
-      (let ((beg (point)))
-        (end-of-line)
-        (delete-region beg (point)))
-      (insert (format "%s: %s %s%%"
-                      (process-get process 'iplayer-id)
-                      (process-get process 'iplayer-state)
-                      (process-get process 'iplayer-progress))))))
+   ((string-match "^finished" string)
+    ;; KLUDGE: get-iplayer installs signal handlers and exit with a 0
+    ;; exit code from them.  That means we can't use the sentinel to
+    ;; distinguish between being killed and exiting with success, so
+    ;; we hack around the problem.
+    (if (= (process-get process 'iplayer-progress) 100)
+        (process-put process 'iplayer-state 'finished)
+      (process-put process 'iplayer-state 'failed)))
+   ((string-match "^exited abnormally" string)
+    (process-put process 'iplayer-state 'failed)))
+  (iplayer-download-display-state process))
 
 (defun iplayer-download ()
   (interactive)
@@ -269,8 +285,12 @@ The presets are defined in the variable `iplayer-presets'."
           (let ((process
                  (start-process "get-iplayer" " *get-iplayer*" "get-iplayer" "--modes=best" "--get" (format "%s" id))))
             (process-put process 'iplayer-id id)
+            (process-put process 'iplayer-state 'connecting)
+            (process-put process 'iplayer-progress 0.0)
             (set-process-filter process 'iplayer-download-process-filter)
-            (display-buffer (get-buffer-create "*iplayer-progress*"))))
+            (set-process-sentinel process 'iplayer-download-process-sentinel)
+            (display-buffer (get-buffer-create "*iplayer-progress*"))
+            (iplayer-download-display-state process)))
       (message "no id at point"))))
 
 (defun iplayer-previous ()