Welcome to the CHICKEN Scheme pasting service

crappy and buggy podcast catcher causing pasted by C-Keen on Fri Nov 30 11:38:16 2012

(use filepath
     args
     miscmacros
     fmt
     srfi-1
     http-client
     rss
     ports
     posix
     files
     progress-indicators
     uri-generic)

(define *feedfile*  "~/.podcasts/feeds")
(define *storage-location* "~/Music/podcasts/")

(define supported-mimes
  '("audio/mpeg" "audio/x-m4a" "audio/x-mp3" "audio/mp3" "application/octet-stream" "application/ogg"))

(define supported-uri-schemes '(http https))

(define (pretty-filesize size)
  (fmt #f (num/si size 1024 "B")))

(define (read/write filename length outport #!optional (bs (* 1024 2)))
  (let ((bar (make-progress-bar frame: (string-append filename ": [~a~a~a~a|~a]") width: 40 max: length)))
    (let loop ((size length))
      (let* ((bytes (min bs size)))
        (if (zero? size) #t
            (let ((input (read-string bytes)))
              (if input
                  (write-string input bytes outport)
                  (when (> bytes 0)
                    (error "Missing ~a from input" (pretty-filesize bytes))))
              (advance-progress-bar! bar bytes)
              (loop (- size bytes))))))
    (finish-progress-bar! bar)))

(define (create-dir/parents file)
  (fold (lambda (d p)
		(let ((new (filepath:join-path (list p d))))
		  (unless (directory-exists? new)
		      (create-directory new))
		  new))
	      ""
	      (filepath:split-path (filepath:drop-file-name file)))
  #t) 					;return something meaningful

(define (make-filename title extension)
  (string-append (string-translate (string-downcase title) " /" "_-") extension))

(define (get-file-port file)
  (file-open file
	     (+ open/wronly open/creat)))

(define (get-length length-string)
  (let ((n (irregex-match-substring
	    (irregex-search '(seq (* whitespace)
				  (submatch (+ num))
				  (* whitespace))
			    length-string) 1)))
    (or (string->number n)
	(error "I need a valid input length instead of" length-string))))

(define (filename-from-url u)
  (filepath:take-file-name u))

(define (download-enclosure item-title enclosure dest catcher)
  (let* ((url (rss:enclosure-url enclosure))
	 (extension (filepath:take-extension (rss:enclosure-url enclosure)))
	 (filename (filename-from-url url))
         (target-location (string-append dest "/" filename))
         (temp-file (string-append target-location ".part"))
         (length (get-length (rss:enclosure-length enclosure)))
         (fd (get-file-port temp-file))
         (outport (open-output-file* fd)))
    (if (equal? catcher 'curl)
        (unless (zero? (system (string-intersperse (list "curl" "-L"  "-o" temp-file url))))
          (printf "\tStill cannot download ~a~%" url)
          (file-close fd)
          (delete-file temp-file))
        (condition-case
         (with-input-from-request
          url
          #f
          (if (zero? length)
              (begin
                (print "Downloading " filename)
                (print "This podcast has no valid length, using simple copy-port w/o output!")
                (lambda () (copy-port (current-input-port) outport)))
              (lambda ()(read/write filename length outport))))
         (e (exn net i/o)
            (printf "~%\tError while downloading ~a to ~a:~%\t~a~%"
                    url
                    filename
                    ((condition-property-accessor 'exn 'message) e))
            (printf "\tRetrying with curl...~%")
            (unless (zero? (system (string-intersperse (list "curl" "-L"  "-o" temp-file url))))
              (printf "\tStill cannot download ~a~%" url)
              (file-close fd)
              (delete-file temp-file)))
         (e (exn http unsupported-uri-scheme)
            (print "Cannot download " url " with http-client, shelling out to curl")
            (unless (zero? (system (string-intersperse (list "curl" "-L"  "-o" temp-file url))))
              (printf "\tUrl scheme ~a is not supported. Cannot download ~a~%"
                      ((condition-property-accessor 'unsupported-uri-scheme 'uri-scheme) e)
                      url)
              (file-close fd)
              (delete-file temp-file)))))
    (when (file-exists? temp-file)
      (print " done.")
      (rename-file temp-file target-location))))

(define (is-new? title url dest)
  (let* ((filename (filepath:join-path
		    (list dest (filename-from-url url)))))
    (create-dir/parents filename)
    (not (file-exists? filename))))

(define (fetch-item i dest catcher)
  (let ((title (rss:item-title i))
        (enclosures (rss:item-enclosures i)))
    (for-each
     (lambda (e)
       (let* ((url (rss:enclosure-url e))
	     (type (rss:enclosure-type e))
	     (file (filename-from-url url)))
	 (if (member type supported-mimes)
	     (if (is-new? title url dest)
                 (download-enclosure title e dest catcher))
	     #t))) ;(printf "\nUnknown enclosure type ~S for title ~S, skipping it.\n" type title))))
     enclosures)))

(define (fetch-podcast! title link dir #!key last-episodes catcher)
  (and-let* ((feed (with-input-from-request link #f rss:read))
             (items/audio-enclosures (filter (lambda (i) (not (null? (rss:item-enclosures i))))
                                             (rss:feed-items feed)))
             (items (if last-episodes
                        (take items/audio-enclosures last-episodes)
                        items/audio-enclosures)))
    (if (null? items) (printf "No items found in RSS feed ~S" title)
        (for-each (lambda (i) (fetch-item i dir catcher)) items))))

(define (load-feeds file)
  (or (ignore-errors (with-input-from-file *feedfile* read)) '()))

(define (add-feed! feed)
  (let ((feeds '())
        (file #f))
    (dynamic-wind
        (lambda ()
          (set! feeds (load-feeds *feedfile*))
          (set! file (open-output-file* (file-open *feedfile*
                                                   (+ open/wronly open/creat)))))
        (lambda ()
                (let* ((old-feeds (or (load-feeds *feedfile*) '()))
                       (new-podcast (retrieve-metadata feed)))
                  (set! feeds (cons new-podcast old-feeds))))
        (lambda ()
          (when (port? file)
            (write feeds file)
            (file-close file))))))

(define options (list (args:make-option
                       (a add)
                       (optional: "URL")
                       "add a feed to the feedfile."
                       (add-feed! arg))))

(define (main)
  (create-dir/parents *feedfile*)
  (create-dir/parents *storage-location*)
  (fmt #t "Reading feeds from " *feedfile*)
  (for-each (lambda (e)
              (fmt #t "\rProcessing podcast " (first e) "\x1b[0K" nl)
              (or (fetch-podcast! (first e)
                                  (second e)
                                  (filepath:join-path (list *storage-location* (third e)))
                                  catcher: (and (>= (length e) 4) (fourth e)))
                  (fmt #t "Could not fetch the whole podcast" nl)))
            (load-feeds *feedfile*))
  (fmt #t "\rdone." "\x1b[0K" nl))

(main)

#|

Causes the following error while downloading episode 20 of
this feed file:

(("Alternativlos" "http://alternativlos.org/alternativlos.rss" "alternativlos"))

Error: call of non-procedure: #

        Call history:

        http-client.scm:128: connections-owner    
        http-client.scm:163: connections          
        http-client.scm:163: hash-table-ref/default       
        http-client.scm:121: open-output-string   
        http-client.scm:121: ##sys#check-output-port      
        http-client.scm:121: uri-common#uri-host          
        http-client.scm:121: ##sys#print          
        http-client.scm:121: ##sys#write-char-0   
        http-client.scm:121: uri-common#uri-port          
        uri-common.scm:153: uri-generic#uri-port          
        uri-common.scm:154: uri-generic#uri-scheme        
        uri-common.scm:154: alist-ref     
        http-client.scm:121: ##sys#print          
        http-client.scm:121: get-output-string    
            [download-enclosure] (k354 (##core#lambda () (##core#let ((kvar348 (and349 (##sys#structure? exvar347 (##core#quote condi......
            [download-enclosure] (##sys#structure? exvar347 (##core#quote condition))     <--

|#

reduced version that works with the file pasted by C-Keen on Sun Dec 2 19:48:33 2012

(use extras http-client progress-indicators filepath)

(define *s* (make-string 2048))

(define o (open-output-file "/tmp/a.mp3"))

(define (read/write filename length outport #!optional (bs (* 1024 2)))
  (let ((bar (make-progress-bar
               frame:
               (string-append filename ": [~a~a~a~a|~a]")
               width:
               40
               max:
               length)))
    (let loop ((size length))
      (let* ((bytes (min bs size)))
        (if (zero? size)
          #t
          (let ((input (read-string! bytes *s*)))
            (if (> input 0)
              (begin
                (write-string *s* bytes outport)
                (advance-progress-bar! bar bytes))
              (when (> bytes 0)
                    (error "Missing ~a from input" (pretty-filesize bytes))))
            (loop (- size bytes))))))
   (finish-progress-bar! bar)))

(define (filename-from-url u) (filepath:take-file-name u))
(define (get-file-port file) (file-open file (+ open/wronly open/creat)))

(define (download-enclosure url length dest catcher)
  (let* ((filename (filename-from-url url))
         (target-location (string-append dest "/" filename))
         (temp-file (string-append target-location ".part"))
         (fd (get-file-port temp-file))
         (outport (open-output-file* fd)))
    (if (equal? catcher 'curl)
      (unless
        (zero? (system
                 (string-intersperse (list "curl" "-L" "-o" temp-file url))))
        (printf "\tStill cannot download ~a~%" url)
        (file-close fd)
        (delete-file temp-file))
      (condition-case
        (with-input-from-request
          url
          #f
          (if (zero? length)
            (begin
              (print "Downloading " filename)
              (print "This podcast has no valid length, using simple copy-port w/o output!")
              (lambda () (copy-port (current-input-port) outport)))
            (lambda () (read/write filename length outport))))
        (e (exn net i/o)
           (printf
             "~%\tError while downloading ~a to ~a:~%\t~a~%"
             url
             filename
             ((condition-property-accessor 'exn 'message) e))
           (printf "\tRetrying with curl...~%")
           (unless
             (zero? (system
                      (string-intersperse
                        (list "curl" "-L" "-o" temp-file url))))
             (printf "\tStill cannot download ~a~%" url)
             (file-close fd)
             (delete-file temp-file)))
        (e (exn http unsupported-uri-scheme)
           (print "Cannot download "
                  url
                  " with http-client, shelling out to curl")
           (unless
             (zero? (system
                      (string-intersperse
                        (list "curl" "-L" "-o" temp-file url))))
             (printf
               "\tUrl scheme ~a is not supported. Cannot download ~a~%"
               ((condition-property-accessor
                  'unsupported-uri-scheme
                  'uri-scheme)
                e)
               url)
             (file-close fd)
             (delete-file temp-file)))))
    (when (file-exists? temp-file)
          (print " done.")
          (rename-file temp-file target-location))))

(download-enclosure  "http://localhost:8888/alternativlos-17.mp3" 89885440 "/tmp" #f)



Minimal testcase (so far) - note that it does *not* read from the http port pasted by sjamaan on Sun Dec 2 20:38:39 2012

(use http-client)

(define *s* (make-string 2048))

(define (read/write length outport)
  (let loop ((size length))
    (let* ((bytes (min 1024 size)))
      (unless (zero? size)
        (write-string *s* bytes outport)
        (loop (- size bytes))))))

(define (download-enclosure url length dest)
  (let* ((target-location (string-append dest "/tempfile"))
         (temp-file (string-append target-location ".part"))
         (outport (open-output-file temp-file)))
    #;(read/write length outport)         ; No bug
    (with-input-from-request url #f (lambda () (read/write length outport))) ; bug
    (when (file-exists? temp-file)
          (print " done.")
          (rename-file temp-file target-location))))

(download-enclosure "http://alternativlos.cdn.as250.net/alternativlos-17.mp3" 89885440 "/tmp")

this again works... added by C-Keen on Sun Dec 2 21:09:49 2012

(use http-client ports)

(define outport (open-output-file "/tmp/f"))
(with-input-from-request
   "http://alternativlos.cdn.as250.net/alternativlos-17.mp3"
   #f 
   (lambda () (copy-port (current-input-port) outport)))

Your annotation:

Enter a new annotation:

Your nick:
The title of your paste:
Your paste (mandatory) :
What does `(string? "foo")' produce?
Visually impaired? Let me spell it for you (wav file) download WAV