ytplay.el/ytplay.el

137 lines
5.5 KiB
EmacsLisp
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

;;; 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)
(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