08 Jan 2015
This continues the code from the part 1.
org-handle-link-youtube
I tried to make the first call to youtube-dl
asynchronous, but it
wasn't working out. So for the current code, there's still about a 2
second delay before the capture buffer appears.
(require 'async)
(defun org-handle-link-youtube (link)
(lexical-let*
((file-name (org-trim
(shell-command-to-string
(concat
"youtube-dl \""
link
"\""
" -o \"%(title)s.%(ext)s\" --get-filename"))))
(dir "~/Downloads/Videos")
(full-name
(expand-file-name file-name dir)))
(add-hook 'org-link-hook
(lambda ()
(concat
(org-make-link-string dir dir)
"\n"
(org-make-link-string full-name file-name))))
(async-shell-command
(format "youtube-dl \"%s\" -o \"%s\"" link full-name))
(find-file (org-expand "ent.org"))
(goto-char (point-min))
(re-search-forward "^\\*+ +Videos" nil t)))
Some notes for people who want to learn more Elisp:
lexical-let*
is needed to have dir
and full-name
visible in the lambda
.
org-make-link-string
is a nice utility command that escapes all sorts of
characters that org-mode
doesn't like, e.g. brackets etc.
You can see my full org-capture
and org-protocol
setup
here.
07 Jan 2015
I'm quite busy with a project today, so I can't compose many words.
However, pasting and explaining some code is fine. The basic idea is
creating TODO tasks in certain org-mode
files by clicking a link in
Firefox, thanks to
org-mode capture.
org-protocol
starter
(require 'org-capture)
(require 'org-protocol)
(setq org-protocol-default-template-key "l")
(push '("l" "Link" entry (function org-handle-link)
"* TODO %(org-wash-link)\nAdded: %U\n%(org-link-hooks)\n%?")
org-capture-templates)
org-wash-link
should clear up some redundancies in the TODO
org-handle-link
should open the appropriate file and heading.
org-link-hooks
should insert some extra information
org-wash-link
Basically, when I capture a question on
Stack Overflow, I
don't want to see - Stack Overflow -
as part of the TODO string,
since the TODO itself is stored in wiki/stack.org/* Questions
.
(defun org-wash-link ()
(let ((link (caar org-stored-links))
(title (cadar org-stored-links)))
(setq title (replace-regexp-in-string
" - Stack Overflow" "" title))
(org-make-link-string link title)))
org-link-hooks
This is just a hack for passing information around that
functions from org-handle-link
can use.
(defvar org-link-hook nil)
(defun org-link-hooks ()
(prog1
(mapconcat #'funcall
org-link-hook
"\n")
(setq org-link-hook)))
org-handle-link
This is the heart of the setup.
(defun org-handle-link ()
(let ((link (caar org-stored-links))
file)
(cond ((string-match "^https://www.youtube.com/" link)
(org-handle-link-youtube link))
((string-match (regexp-quote
"http://stackoverflow.com/") link)
(find-file (org-expand "wiki/stack.org"))
(goto-char (point-min))
(re-search-forward "^\\*+ +Questions" nil t))
(t
(find-file (org-expand "ent.org"))
(goto-char (point-min))
(re-search-forward "^\\*+ +Articles" nil t)))))
- Youtube links will be handled with
org-handle-link-youtube
- Stack Overflow links will be stored in
wiki/stack.org/* Questions
- all other links will be stored in
ent.org/* Articles
I'll write down org-handle-link-youtube
in a later post, since I
would still like to sort out a few kinks with it. The main issue is
that I'm sending two requests to Youtube: one to download the video,
which is fine, since async
handles it; and other to get the title of
the video and put it in the heading. And this other request causes a
perceptible delay when capturing.
06 Jan 2015
And you're rushing headlong out of control...
-- Brian May
I've finally wrapped a piece of config that I was using for a while in
a package called headlong.
What does it do?
It provides a macro called headlong-with
that modifies minibuffer
completion for the forms within it, making it faster in some
situations. For instance:
(headlong-with
(completing-read "Jump to bookmark: "
bookmark-alist nil t))
or:
(headlong-with (read-extended-command))
But more importantly, it provides two commands that can use it
efficiently: headlong-bookmark-jump
and
headlong-bookmark-jump-other
. The second one is basically the same
as the first one, except it opens the bookmark with pop-to-buffer
,
i.e. in other window.
How does this completion work?
It's nothing fancy, you will just exit the minibuffer automatically as
soon as there is only one completion candidate left.
So it saves you one keystroke, namely RET.
How much is one keystroke worth? It depends.
If you arrange your bookmarks in a way that I do, with each one
starting with a different letter, it saves you 33% of the total
keystrokes. For example, suppose I have:
(global-set-key (kbd "M-p") 'bookmark-jump)
(global-set-key (kbd "M-o") 'headlong-bookmark-jump)
Then I can jump to my bookmarked directory named "s: sources"
with
two methods:
The second method looks like it's 33% shorter, but it feels like it's
even more, since pressing RET is harder than it should be
on most keyboards.
Why is this cool?
This is cool because you can implement your bookmarks as efficiently
as you would with just wrapping stuff with a lambda
and using
global-set-key
, except that you can view and edit the bindings with
bookmark-bmenu-list
, and quickly the update bookmark positions with
bookmark-set
.
Here's what I get when I call M-x bookmark-bmenu-list
:
In the list above:
- black bookmarks are files
- blue bookmarks are directories
- pink bookmarks are functions (you need
bookmark+
for them)
The package should be available in MELPA soon.
05 Jan 2015
If you haven't been living under a rock, you already know what
Youtube is. It's a repository with videos of
varying degree of usefulness with a terrible media player tacked on.
Instead, I like to watch my videos in
VLC, which comes closest to
providing an Emacs-like experience among video players.
Useful VLC shortcuts
Here is a list of shortcuts that really make me stick with VLC:
- f - toggle full-screen
- b - toggle audio track
- n - toggle subtitle track
- ] - speed up play by
0.1
- [ - slow down play by
0.1
- M-right - forward by 15 seconds
- M-left - backward by 15 seconds
- C-right - forward by 60 seconds
- C-left - backward by 60 seconds
- M-1 - quarter of video size
- M-2 - half of video size
- M-3 - full video size
- M-4 - double video size
- C-h - toggle mouse buttons
So if you're not watching instructional videos at 1.6
speed,
or skipping the Simpsons intro sequence with M-right, you're missing out.
From Youtube to VLC
youtube-dl is an excellent command-line tool
for saving the videos from Youtube. It downloads the highest resolution at a usually
higher speed than Youtube's player buffers. I've discovered it when I had to download
a bunch of lecture videos from edX.
You can install it with:
sudo pip install youtube-dl
One Emacs script to rule them all
I quickly tired of opening a shell, setting the directory, entering
the command, and pasting the link. So I wrote some Elisp code that does it for me.
It's nothing too sophisticated, but I've been using this version for a couple months:
(defun youtube-dl ()
(interactive)
(let* ((str (current-kill 0))
(default-directory "~/Downloads")
(proc (get-buffer-process (ansi-term "/bin/bash"))))
(term-send-string
proc
(concat "cd ~/Downloads && youtube-dl " str "\n"))))
How it works:
- Copy the link in Firefox
- M-x
youtube-dl
.
That's it. A new *ansi-term*
will open with the task of downloading the video from the link
in the clipboard to ~/Downloads
.
I don't have to wait for the download to finish and can immediately open the video from dired
.
See the previous post for the description of
dired
process-starting setup. I can stack up multiple downloads at once if I wish in different
*ansi-term*
s.
This is my script. There are many like it, but this one is mine.
I did an internet search before writing this post. Apparently many
others had the same idea of integrating youtube-dl
into Emacs. You
can use mine or any other code to generate a setup that works for you.
For instance, for a while, instead of copy-pasting the URL and calling
youtube-dl
I used to just click the
org-mode capture
button in Firefox, and it would automatically create a TODO
item,
download the video, and put the link to the downloaded video in the
TODO
.
I've dropped this workflow when
the yank bug
surfaced. I don't yet have enough experience of working with Emacs's C
code to fix it. Although, according to this
excellent rant,
fixing the bug is only half of the problem: getting it merged is hard.
I'll see how it goes with my latest tiny patch.
So far it has been ignored, but it is only two days old as of now.
04 Jan 2015
Here are the standard dired
functions for starting processes:
- ! calls
dired-do-shell-command
- & calls
dired-do-async-shell-command
While the second one is usually better than the first one, having the
benefit of not locking up Emacs, it's still not convenient enough for
me. The reason is pretty simple: I want to keep the processes that I
started even when I close Emacs (like opened PDFs or videos). This is
a non-issue for people with months-long emacs-uptime
, but for me an
Emacs session lasts on the order of hours, since I mess about with
Elisp a lot. Below, I'll share some of my dired
process-related
customizations.
Ignore running processes when closing Emacs
Usually there's nothing wrong with just killing a spawned process,
like an ipython
shell or something.
;; add `flet'
(require 'cl)
(defadvice save-buffers-kill-emacs
(around no-query-kill-emacs activate)
"Prevent \"Active processes exist\" query on exit."
(flet ((process-list ())) ad-do-it))
Guess programs by file extension
With this setup, usually there's no need to manually type in the
command name.
(require 'dired-x)
(setq dired-guess-shell-alist-user
'(("\\.pdf\\'" "evince" "okular")
("\\.\\(?:djvu\\|eps\\)\\'" "evince")
("\\.\\(?:jpg\\|jpeg\\|png\\|gif\\|xpm\\)\\'" "eog")
("\\.\\(?:xcf\\)\\'" "gimp")
("\\.csv\\'" "libreoffice")
("\\.tex\\'" "pdflatex" "latex")
("\\.\\(?:mp4\\|mkv\\|avi\\|flv\\|ogv\\)\\(?:\\.part\\)?\\'"
"vlc")
("\\.\\(?:mp3\\|flac\\)\\'" "rhythmbox")
("\\.html?\\'" "firefox")
("\\.cue?\\'" "audacious")))
Add nohup
According to info nohup
:
`nohup' runs the given COMMAND with hangup signals ignored, so that
the command can continue running in the background after you log
out.
In my case, it means that the processes started by Emacs can
continue running even when Emacs is closed.
(require 'dired-aux)
(defvar dired-filelist-cmd
'(("vlc" "-L")))
(defun dired-start-process (cmd &optional file-list)
(interactive
(let ((files (dired-get-marked-files
t current-prefix-arg)))
(list
(dired-read-shell-command "& on %s: "
current-prefix-arg files)
files)))
(let (list-switch)
(start-process
cmd nil shell-file-name
shell-command-switch
(format
"nohup 1>/dev/null 2>/dev/null %s \"%s\""
(if (and (> (length file-list) 1)
(setq list-switch
(cadr (assoc cmd dired-filelist-cmd))))
(format "%s %s" cmd list-switch)
cmd)
(mapconcat #'expand-file-name file-list "\" \"")))))
The dired-filelist-cmd
is necessary because vlc
weirdly
doesn't make a playlist when given a list of files.
Then I bind it to r - a nice shortcut not bound
by default in dired
:
(define-key dired-mode-map "r" 'dired-start-process)