Recently Written · git

librivox

Automated youtube uploading of audiobooks

git clone https://github.com/equwal/librivox

Log | Files | Refs


commit 126a6fb65b52e2bb95aee94f42f6b740776cbd63
Spenser Truex <struex0@gmail.com>
2018-07-31 10:02:01 -0700

ffmpeg mp3 to mp4 via bash interaction.

 README.md            |  5 +++++
 librivox.asd         |  6 ++++--
 persistent.lisp      |  0
 rss.asd              |  6 ++++++
 rss/rss.lisp         | 27 +++++++++++++++++++++++++++
 src/ffmpeg.lisp      | 46 ++++++++++++++++++++++++++++++++++++++++++++++
 src/librivox.lisp    |  5 ++++-
 utils.lisp           |  7 +++++++
 youtube.asd          |  6 ++++++
 youtube/utils.lisp   |  1 +
 youtube/youtube.lisp |  3 +++
 11 files changed, 109 insertions(+), 3 deletions(-)
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..c2ed1a9
--- /dev/null
+++ b/README.md
@@ -0,0 +1,5 @@
+# Requirements:
+`mp3info`
+`ffmpeg`
+`bash`
+`Quicklisp`
diff --git a/librivox.asd b/librivox.asd
index 0770788..86176dd 100644
--- a/librivox.asd
+++ b/librivox.asd
@@ -1,6 +1,8 @@
 (asdf:defsystem :librivox
   :serial t
-  :depends-on (:drakma :rss :youtube)
+  :depends-on (:drakma :uiop :rss :youtube)
   :description "Librivox auto downloader/youtube uploader."
   :components ((:file "utils")
-	       (:file "src/librivox")))
+	       (:file "src/librivox")
+	       (:file "src/ffmpeg")))
+
diff --git a/persistent.lisp b/persistent.lisp
new file mode 100644
index 0000000..e69de29
diff --git a/rss.asd b/rss.asd
new file mode 100644
index 0000000..c34414c
--- /dev/null
+++ b/rss.asd
@@ -0,0 +1,6 @@
+(asdf:defsystem :rss
+  :serial t
+  :description "RSS feed reader."
+  :depends-on (:drakma :cl-feedparser)
+  :components ((:file "utils")
+	       (:file "rss/rss")))
diff --git a/rss/rss.lisp b/rss/rss.lisp
new file mode 100644
index 0000000..226b3f9
--- /dev/null
+++ b/rss/rss.lisp
@@ -0,0 +1,27 @@
+(defpackage :rss
+  (:use :cl :drakma :utils :cl-feedparser)
+  (:import-from :drakma
+		:http-request)
+  (:import-from :utils
+		:remove-after)
+  ;(:import-from :feedparser)
+  ;(:export )
+  )
+(in-package :rss)
+(defvar *persistence* "persistent.lisp")
+#|(let ((x (parse-feed "https://librivox.org/rss/latest_releases")))
+   (loop for hk being the hash-key in x
+      for hv being the hash-value in x
+      collect (cons hk hv)))
+(defun parse-feed (url)
+  (feedparser:parse-feed (http-request url)))
+(defun update (url)
+  (remove-after (previous-update)
+		(readable-feed (parse-feed url))
+		:test #'equal))|#
+(defun previous-update ()
+  "Get the most recent update."
+  (with-open-file (s *persistence*
+		     :direction :input
+		     :if-does-not-exist :create)
+    (read s nil nil)))
diff --git a/src/ffmpeg.lisp b/src/ffmpeg.lisp
new file mode 100644
index 0000000..044f3e3
--- /dev/null
+++ b/src/ffmpeg.lisp
@@ -0,0 +1,46 @@
+(in-package :librivox)
+(defvar *bash-output* *debug-io*)
+(defparameter *seconds* 0 "The time in seconds of the entire book.")
+(defvar *downloads-dir* "/home/jose/common-lisp/librivox/downloads/")
+(defun run-lines (&rest code-strings)
+  (mapcar #'(lambda (x) (run-program (format nil x *downloads-dir*) :output *bash-output*))
+	  code-strings))
+(defun nth-digit (digit number base)
+  (if (= 0 digit)
+      (mod number base)
+      (nth-digit (1- digit) (floor number base) base)))
+(defun hours (seconds)
+  (/ (- seconds (* 60 (minutes seconds)) (seconds seconds))
+     60 60))
+(defun minutes (seconds)
+  (nth-digit 1 seconds 60))
+(defun seconds (seconds)
+  (nth-digit 0 seconds 60))
+
+(defun convert (image-pathname output-pathname)
+  "Convert the zip to video."
+  ;; ~A for the download directory, ~:*~A after the first.
+  (let ((output-pathname (merge-pathnames output-pathname *downloads-dir*)))
+    (unwind-protect (let ((image-pathname (merge-pathnames image-pathname *downloads-dir*)))
+		      (run-lines "unzip ~A*.zip -d ~:*~A"
+				 "for f in ~A*.mp3; do echo \"file '$f'\" >> ~:*~Aconcat.txt; done"
+				 "ffmpeg -f concat -safe 0 -i ~Aconcat.txt -c copy ~:*~Aoutput1.mp3")
+		      (setf *seconds* (parse-integer (run-program (format nil "mp3info -p \"%S\" ~Aoutput1.mp3" *downloads-dir*) ;
+								  :output 'string)
+						     :junk-allowed t))
+		      (run-lines (format nil "ffmpeg -loop 1 -i ~A -t ~D:~D:~D -c mjpeg ~~Atemp.mp4" ;
+					 image-pathname
+					 (hours *seconds*)
+					 (minutes *seconds*)
+					 (seconds *seconds*)))
+	       
+		      (run-lines (format nil"ffmpeg -i ~~Atemp.mp4 -i ~~:*~~Aoutput1.mp3 -c copy ~~:*~~Aconverted/~A"
+					 output-pathname)
+				 "rm ~Atemp.mp4"
+				 "rm ~A*.mp3"
+				 "rm ~Aconcat.txt"))
+      (run-lines "rm ~Atemp.mp4"
+		 "rm ~A*.mp3"
+		 "rm ~Aconcat.txt"))))
+;;; Example input: (convert "image.jpg" "final-test-probably.mp4")
+
diff --git a/src/librivox.lisp b/src/librivox.lisp
index 5f3f857..c12a024 100644
--- a/src/librivox.lisp
+++ b/src/librivox.lisp
@@ -1,3 +1,6 @@
 (defpackage :librivox
-  (:use :cl :drakma :utils))
+  (:use :cl :drakma :utils :uiop)
+  (:import-from :uiop
+		:run-program))
 (in-package :librivox)
+(defvar *url* "https://librivox.org/rss/latest_releases")
diff --git a/utils.lisp b/utils.lisp
new file mode 100644
index 0000000..2278d16
--- /dev/null
+++ b/utils.lisp
@@ -0,0 +1,7 @@
+(defpackage :utils
+  (:use :cl :cl-user)
+  (:export :remove-after))
+(in-package :utils)
+
+(defun remove-after (item list &key (test #'eql))
+  (subseq list (search (list item) list :test test)))
diff --git a/youtube.asd b/youtube.asd
new file mode 100644
index 0000000..9bae284
--- /dev/null
+++ b/youtube.asd
@@ -0,0 +1,6 @@
+(asdf:defsystem :youtube
+  :serial t
+  :description "Youtube data api."
+  :depends-on (:drakma)
+  :components ((:file "utils")
+	       (:file "youtube/youtube")))
diff --git a/youtube/utils.lisp b/youtube/utils.lisp
new file mode 120000
index 0000000..5b43057
--- /dev/null
+++ b/youtube/utils.lisp
@@ -0,0 +1 @@
+utils.lisp
\ No newline at end of file
diff --git a/youtube/youtube.lisp b/youtube/youtube.lisp
new file mode 100644
index 0000000..2e0a9b2
--- /dev/null
+++ b/youtube/youtube.lisp
@@ -0,0 +1,3 @@
+(defpackage :youtube
+  (:use :cl :drakma :utils))
+(in-package :youtube)