summaryrefslogtreecommitdiff
path: root/downloader.lisp
blob: e9b8ffb61ebe995417548c050e190b62ce710b21 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
;;;; downloader.lisp

(in-package #:vampire)

(defvar *dl-cluster*)
(defvar *media-directory*)

(defun start-downloader-service (config)
  (let ((media-dir
          (merge-pathnames "media/" (static-directory config)))) 
    (ensure-directories-exist media-dir)
    (setf *dl-cluster* (legion:make-cluster (downloader-threads config)
                                            (lambda (job) (funcall job media-dir)))))
  (legion:start *dl-cluster*))

(defun add-fetch-track-job (url ok err)
  "URL is a url to some audio track. OK is a function that accepts a
  TRACK instance. ERR is a function accepting an error condition"
  (legion:add-job
   *dl-cluster*
   (lambda (media-dir)
     (handler-case
         (funcall ok (download-media url media-dir))
       (error (e) (funcall err e))))))

(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|)
               (track-title :|track|)
               (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 (or track-title title)
       :album album
       :codec codec
       :artist artist
       :duration dur
       :thumb-url url))))

(defun download-media (url media-dir)
  "Download media and create a new track from its audio source, moving
   the raw audio to the media-dir when done."
  (with-temp-dir (tmpdir)
    (let* ((tmpname
             (default-name "media"))
           (trackinfo-file
             (trackinfo-file tmpdir tmpname)))
      (uiop:run-program 
       (format nil "youtube-dl --playlist-end 1 --write-info-json -x -o \"~a/~a.%(ext)s\" '~a'"
               tmpdir tmpname url))
      (let* ((info
               (trackinfo trackinfo-file))
             (downloaded
               (trackmedia-file tmpdir))
             (file
               (merge-pathnames (format nil "~a.~a"
                                        (nuid) (pathname-type downloaded))
                                media-dir)))
        (uiop:copy-file downloaded file)
        (new-track file info)))))