137 lines
5.5 KiB
EmacsLisp
137 lines
5.5 KiB
EmacsLisp
;;; 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 video’s 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
|