ytplay.el/ytplay.el

137 lines
5.5 KiB
EmacsLisp
Raw Normal View History

;;; ytplay.el --- Play YouTube videos in mpv -*- lexical-binding: t -*-
;; Author: Lucien Cartier-Tilet <lucien@phundrak.com>
;; Maintainer: Lucien Cartier-Tilet <lucien@phundrak.com>
;; Version: 0.1.0
;; Package-Requires: ((emacs "25.1") (s "1"))
;; Homepage: https://labs.phundrak.com/phundrak/ytplay.el
;; Keywords: convenience
;; This file is not part of GNU Emacs
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; commentary
;;; Code:
(require 'cl-lib)
(require 's)
(require 'seq)
2021-07-01 09:37:16 +00:00
(defgroup ytplay ()
"Play youtube videos in a video player through Emacs."
:prefix "ytplay-"
:link '(url-link :tag "Gitea" "https://labs.phundrak.com/phundrak/ytplay.el"))
(defcustom ytplay-video-player "mpv"
"Executable for playing videos."
:group 'ytplay
:type 'executable
:version "0.1.0")
(defmacro ytplay--rx-extra (&rest body-forms)
"Extra keywords for `rx' that can be used in BODY-FORMS."
`(rx-let ((resolution (seq (+ digit) "p" (0+ digit)))
(size (seq (+ digit) (opt "." (* digit)) (or "K" "M" "G") "iB")))
,@body-forms))
(defun ytplay--get-all-formats-raw (video)
"Get the raw lines describing available formats for VIDEO."
(cl-remove-if-not
(lambda (line)
(string-match (ytplay--rx-extra (rx (one-or-more digit) "x" (one-or-more digit)
(+ blank)
resolution
(+ ascii)
size))
line))
(s-lines (with-temp-buffer
(call-process "youtube-dl" nil t nil "--list-formats" video)
(buffer-string)))))
(cl-defstruct ytplay--format
"Representation of an available format for a video."
format-code extension resolution total-size)
(defun ytplay--raw-format-to-struct (formats)
"Get normalized formats from a videos raw FORMATS."
(cl-remove-if #'null
(mapcar (lambda (format)
(save-match-data
(string-match (ytplay--rx-extra
(rx
(group (+ digit))
(one-or-more space)
(group (+ alnum))
(one-or-more space)
(+ alnum)
(one-or-more space)
(group resolution)
(one-or-more ascii)
(one-or-more space)
(group size)))
format)
(let ((format-code (match-string-no-properties 1 format))
(extension (match-string-no-properties 2 format))
(resolution (match-string-no-properties 3 format))
(total-size (match-string-no-properties 4 format)))
(when (and format-code extension resolution total-size)
(make-ytplay--format :format-code format-code
:extension extension
:resolution resolution
:total-size total-size)))))
formats)))
(defun ytplay--chose-resolution (formats)
"FORMATS."
(let* ((resolutions (seq-uniq (mapcar (lambda (format)
(ytplay--format-resolution format))
formats)))
(user-choice (completing-read "Resolution: " resolutions)))
(cl-remove-if-not (lambda (format)
(equal (ytplay--format-resolution format)
user-choice))
formats)))
(defun ytplay--chose-size (formats)
"FORMATS."
(let ((formats (mapcar (lambda (format)
`(,(ytplay--format-total-size format) . ,format))
formats)))
(cdr (assoc (completing-read "File: " formats) formats))))
;;;###autoload
(defun ytplay (video)
"Play in mpv an internet VIDEO."
(interactive "sVideo url: ")
(let ((code (ytplay--format-format-code
(ytplay--chose-size
(ytplay--chose-resolution
(ytplay--raw-format-to-struct
(ytplay--get-all-formats-raw video)))))))
(with-temp-buffer
(async-shell-command (format "%s --ytdl-format=%s+bestaudio/best \"%s\""
ytplay-video-player
code
video)))))
(provide 'ytplay)
;;; ytplay.el ends here