diff options
Diffstat (limited to 'emacs/.emacs.d/lisp/my/my-ytdl.el')
-rw-r--r-- | emacs/.emacs.d/lisp/my/my-ytdl.el | 103 |
1 files changed, 94 insertions, 9 deletions
diff --git a/emacs/.emacs.d/lisp/my/my-ytdl.el b/emacs/.emacs.d/lisp/my/my-ytdl.el index 9118493..7cdda43 100644 --- a/emacs/.emacs.d/lisp/my/my-ytdl.el +++ b/emacs/.emacs.d/lisp/my/my-ytdl.el @@ -26,6 +26,7 @@ ;;; Code: +(require 'tor) (defvar my-ytdl-program "yt-dlp") @@ -60,21 +61,95 @@ ;; "%(id)s.%(ext)s" ;; for long names "%(playlist|.)s/%(playlist_index|)s%(playlist_index&-|)s%(title)s.%(ext)s" "--write-description" + "--write-info-json" "--write-thumbnail")) (defvar my-ytdl-audio-download-dir "~/Downloads" "Directory for ytdl to download audios to.") +(defvar my-ytdl-music-download-dir "~/Downloads" + "Directory for ytdl to download music to.") + (defun my-ytdl-internal (urls type &optional no-tor) - (my-with-default-directory (if (eq type 'video) - my-ytdl-video-download-dir - my-ytdl-audio-download-dir) - (apply 'my-start-process-with-torsocks - (append - (list no-tor (format "ytdl-%s" urls) (format "*ytdl-%s*" urls) - my-ytdl-program) - (if (eq type 'video) my-ytdl-video-args my-ytdl-audio-args) - (split-string urls))))) + (my-with-default-directory (pcase type + ('video my-ytdl-video-download-dir) + ('audio my-ytdl-audio-download-dir) + ('music my-ytdl-music-download-dir) + (_ (error "Unsupported type: %s" type))) + (set-process-sentinel + (apply 'my-start-process-with-torsocks + (append + (list no-tor (format "ytdl-%s" urls) (format "*ytdl-%s*" urls) + my-ytdl-program) + (if (eq type 'video) my-ytdl-video-args my-ytdl-audio-args) + (split-string urls))) + (lambda (proc event) + (let ((status (process-exit-status proc))) + (if (eq status 0) + (progn + (message "ytdl-%s %s: DONE" type urls)) + (message "ytdl-%s %s FAILED: %s" type urls event))))))) + +(defun my-ytdl-video-info (url) + "Given a video URL, return an alist of its properties." + (with-temp-buffer + (call-process my-ytdl-program nil t nil "--no-warnings" "-j" url) + (let ((start (point))) + (call-process-region + nil nil "jq" nil t nil + "pick(.webpage_url, .fulltitle, .channel_url, .channel, .channel_follower_count, .thumbnail, .duration_string, .view_count, .upload_date, .like_count, .is_live, .was_live, .categories, .tags, .chapters, .availability, .uploader, .description)") + (goto-char start) + (json-read))) + ) + +(defun my-ytdl-video-url-p (url) + (let ((urlobj (url-generic-parse-url url))) + (or (and (string-match-p + "^\\(www\\.\\|m\\.\\)?\\(youtube\\.com\\|yewtu\\.be\\)" + (url-host urlobj)) + (string-match-p "^/watch\\?v=.*" (url-filename urlobj))) + (equal "youtu.be" (url-host urlobj))))) + +(require 'hmm) +(defvar my-ytdl-player 'hmm-external-mpv "Function to play ytdl urls.") + +(defun my-ytdl-video-format-seconds (secs) + (setq secs (floor secs)) + (if (>= secs 3600) + (format "%d:%02d:%02d" + (/ secs 3600) (/ (% secs 3600) 60) (% secs 60)) + (format "%d:%02d" + (/ secs 60) (% secs 60)))) + +(defun my-ytdl-video-format-chapters (chapters) + (mapconcat + (lambda (chapter) + (let-alist chapter + (format "%s: %s-%s" .title (my-ytdl-video-format-seconds .start_time) + (my-ytdl-video-format-seconds .end_time)))) + chapters + "; ")) + +(defun my-ytdl-video-render-info (info url) + (setf (alist-get 'webpage_url info) + (concat (alist-get 'webpage_url info) + " -- " (buttonize "play" (lambda (_) + (funcall my-ytdl-player url))) + " " (buttonize "context" + (lambda (_) + (funcall my-url-context-function url)))) + (alist-get 'chapters info) + (my-ytdl-video-format-chapters (alist-get 'chapters info))) + (infobox-render + (infobox-translate info (infobox-default-specs info)) + `(my-ytdl-video-infobox ,url) + (called-interactively-p 'interactive))) + +(defun my-ytdl-video-infobox (url) + (interactive "sytdl video url: ") + ;; Remove any extra queries from the URL + (setq url (replace-regexp-in-string "&.*" "" url)) + (my-ytdl-video-render-info (my-ytdl-video-info url) url)) ;;; fixme: autoload (defun my-ytdl-video (urls) @@ -87,11 +162,21 @@ (interactive "sURL(s): ") (my-ytdl-internal urls 'audio)) +(defun my-ytdl-music (urls) + "Download music with ytdl." + (interactive "sURL(s): ") + (my-ytdl-internal urls 'music)) + (defun my-ytdl-audio-no-tor (urls) "Download audio with ytdl." (interactive "sURL(s): ") (my-ytdl-internal urls 'audio t)) +(defun my-ytdl-music-no-tor (urls) + "Download music with ytdl." + (interactive "sURL(s): ") + (my-ytdl-internal urls 'music t)) + ;;; fixme: autoload (defun my-ytdl-video-no-tor (urls) "Download videos with ytdl." |