For the English part of the blog, see Content AND Presentation.

2024-06-17 Selecting subtitles’ language with mpv and Emacs hacking

As you know quite well if you’re following my blog, I watch Doctor Who regularly. These days, I mostly use mpv for video viewing (unless I watch streamed videos on a certain proprietary platform, which I don’t like, but it seems the only way to watch the newest season). My usual workflow is to buy dvds (which have a great advantage over streaming – I actually, physically own them), rip them to mp4​s and then spend some time watching, translating, screenshotting and doing whatever I want with them.

One issue I have is that for most episodes, I have two versions of the srt file with subtitles – an English one and a Polish one, and I’d like to be able to select either of them easily.

At first, I wanted to write a small convenience Elisp command enabling me to choose which subtitle file to open when starting mpv in Dired. I started to read the mpv manual to find out how you can open the video with a given subtitle file, and I learned that you can tell mpv to read several subtitle files and then cycle through them interactively with j and J. That’s even better, I thought! I can just issue the --sub-file option several times and offload the work onto mpv. Then I learned about the --sub-auto option, and it turned out that I don’t even need to do that! The only thing left to do is to rename my mp4 and srt files. For example, assume that I have the s05e02-the-beast-below.mp4 file. If I rename the subtitles to and s05e02-the-beast-below.en.mp4, mpv will automatically load them both and let me switch the language by pressing j. That’s pretty cool!

It gets even better! This naming scheme is apparently supported by Subed mode, so it correctly opens file.mp4 in mpv when I visit That’s great!

The only thing left is to tell Emacs that I want to use mpv to open mp4 files. One way to do it is to add ("\\.mp4\\'" "mpv") to the dired-guess-shell-alist-user variable. This makes it so pressing & and then RET on a file in Dired will automatically open it in mpv.

That’s two keypresses, though. (Three if you count shift.) Can we do better? I never want to visit an mp4 file in Emacs, so why not configure it so that pressing RET on an mp4 file will open it in mpv? (By the way, you can also press W on a file, which will open it in the browser. That actually works for videos, although obviously the browser UI is far inferior to mpv’s.)

It turns out that the situation is pretty complicated. One thing I found that I thought would help me was the dired-open package. It contains several features which allow you to open files from Dired in various ways, including in external applications. If you include ("mp4" . "mpv") in the dired-open-extensions alist, pressing RET on an mp4 file in Dired will open it using mpv. (Under the hood, it’s using the dired-open-file function, which in turn uses the dired-open-functions hook, which has dired-open-by-extension as one of its functions.)

However, this uses dired-open--start-process, which – for reasons unknown to me – uses file-truename. The problem with that function is that it follows symlinks. In my setup, subtitle files reside in one directory and video files in another, and the latter are symlinked to the directory where the former are stored. This is perfectly reasonable: the subtitles are in the directory included in my backup scripts (since it is me who translates them, and I want my work to be backed up!), but there is no point in making backups of huge video files (I have physical dvds and a flash drive with all the episodes, so I don’t need yet another copy…).

Now that I’ve learned about dired-open, however, I really like it – I just don’t want it to follow symlinks. What can I do?

The best thing to do is to file an issue (and offer to fix it myself), which I did. Until this is resolved, I can (obviously) hack the code of dired-open – this will work until the next update of the dired-open package, and then (hopefully) a fix will have landed, so it’s ugly, but functional. That’s exactly what I did. The trouble with this “solution” is that file-truename was probably used there for a reason, and it’s quite possible that I demolished some Chesterton’s fence this way. I’ll probably live with that unless it breaks something. If it does, I have one more trick up my sleeve. It’s a rather extreme hack, but should be doable. Buckle your seatbelts: I could advise dired-open--start-process, using the :around advice combinator, then advice file-truename before running the original dired-open--start-process, effectively calling expand-file-name instead, and remove that advice after dired-open--start-process returns. In other words, I’d have advice setting and then removing another advice. Do not try this at home, kids;-)!

CategoryEnglish, CategoryBlog, CategoryEmacs

Comments on this page

2024-06-08 Managing Firefox tabs from command line with brotab

It’s a real shame that you can’t drive Firefox from command line. (In fact, I already complained about it on this very blog.) In an ideal world, you could say firefox --eval '...' and put some (possibly) JavaScript code there to do stuff like navigate to a page, get the URL of the current page, get the list of open tabs etc.

Even in the simplest case I needed – getting information about the current tab – the situation is quite difficult. I spent a considerable amount of time trying to do this in a reliable way, and I still failed. I even fished through the Firefox profile directory to see if there is some obscure json file or sqlite database which holds that information, but I didn’t find it anywhere.

Interestingly, something like what I need (sort of) possible, using a tool called Marionette. It doesn’t seem very well suited for this particular job, however – it’s meant for testing, not regular usage. Technically, you can start Firefox with the --marionette command line parameter and use it (more or less) normally, but I’m not sure about e.g. security implications. Also, the address bar gets a “robot” icon (apparently meaning that this Firefox is not meant to be driven by a human), and (which is probably the most annoying thing) you don’t seem to have access to the saved login data and passwords. With all that in mind, the fact that you need to use Python to drive the Marionette-enabled Firefox seems the least worry (as you may have guessed, I’m not a Python fan).

Fortunately, there is another solution, which is much less comprehensive, but sufficient for quite a few use cases: BroTab. It is a simple command line tool, accompanied by a browser extension, which lets you access your browser’s tabs from command line. It does not allow to do a lot, but it is more than enough for my needs – that is, getting information about the state of Firefox (like how many tabs are open or what the current tab is). If you ever need to control (some aspects) of a brwoser from the command line or get some information about the open tabs, go and check out BroTab – it’s really useful! (And by the way, it seems that it does support at least one other browser besides Firefox.)

CategoryEnglish, CategoryBlog

Comments on this page

2024-06-03 Symlinks to directories

It’s been a long time since I knew that symlinks to directories are tricky. Recently I decided to look into it more closely and learn once and for all what is going on with them.

Here is what I learned. Assume the directory structure created by the following commands.

mkdir -p /tmp/a/b
mkdir /tmp/z
ln -s /tmp/a/b /tmp/z/y

And let’s move to the symlinked directory.

cd /tmp/z/y

Now a few interesting things happen. If you type cd ../ and press tab, the directory name y will be autocompleted. However, if you type ls .., you will see the directory b – no trace of y.

What is going on???

Well, let’s start with the easy part. The /tmp/z/y directory is really /tmp/a/b, so .. in it points in reality to /tmp/a. That’s why ls .. showed the contents of /tmp/a.

But the shell thinks that you are in /tmp/z/y – for example, if your shell prompt is set up to show the current directory (which it is by default on most systems), it doesn’t show /tmp/a/b but /tmp/z/y. This means that if you type cd .. and press enter, it will strip one component from /tmp/z/y and land you in /tmp/z. A similar thing happens if you press tab instead – it will suggest autocompletions from /tmp/z.

This is not the whole story, though. bash man page mentions a -P option to cd, which causes it to resolve symlinks before processing .. entries. Thus, when in /tmp/z/y, cd -P .. will go to /tmp/a. However, the same man page mentions also -L, which “forces symbolic links to be followed by resolving the link after processing instances of ..~”. Does that mean that ~-L is the default behavior? That is left a bit vague. Happily, man pages are not everything – there is also the info manual, which does say explicitly that -L is the default.

The takeaway from all of it is probably that it’s better to avoid symlinking directories, and if (for some reason) you must do that, I’d strongly consider writing some scripts or shell aliases to work with them so that you’ll never need to manually enter or leave them.

That’s it for today!

CategoryEnglish, CategoryBlog

Comments on this page