;;;; downloader.lisp (in-package #:vampire) (defvar *dl-cluster* nil) (defun start-downloader-service (&key (count 5)) (setf *dl-cluster* (or *dl-cluster* (legion:make-cluster count 'run-job))) (legion:start *dl-cluster*)) (defun run-job (thunk) (funcall thunk)) (defun add-fetch-track-job (url ok err) (legion:add-job *dl-cluster* (lambda () (if-let (track (download-media url)) (funcall ok track) (funcall err url))))) (defun trackinfo-file (dir name) (merge-pathnames (format nil "~a.info.json" name) dir)) (defun trackmedia-file (dir) (find-if (lambda (path) (not (string-equal "json" (pathname-type path)))) (uiop:directory-files dir))) (defun trackinfo (path) "Returns a trackinfo object - a list whose first member is a path to a file and whose CDR is a PLIST of attributes to pass to (make-instance 'track ...)" (with-plist ((title :|title|) (album :|album|) (codec :|acodec|) (artist :|artist|) (dur :|duration|) (thumbs :|thumbnails|) (source :|webpage_url|)) (jonathan:parse (alexandria:read-file-into-string path)) (with-plist ((url :|url|)) (first thumbs) (list :source source :title title :album album :codec codec :artist artist :duration dur :thumb-url url)))) (defun download-media (url) (with-temp-dir (tmpdir) (handler-case (let* ((tmpname (default-name "media")) (trackinfo-file (trackinfo-file tmpdir tmpname))) (uiop:run-program (format nil "youtube-dl --write-info-json -x -o \"~a/~a.%(ext)s\" ~a" tmpdir tmpname url)) (when (uiop:file-exists-p trackinfo-file) (let ((info (trackinfo trackinfo-file))) (new-track (trackmedia-file tmpdir) info)))) (error (e) (format t "ERROR: ~a~%" e) nil))))