Search code examples
luafzfmpv

Use fdfind and fzf to open files in mpv


kdialog-open-files.lua and zenity-open-files.lua are two projects that use KDE KDialog and Gnome's zenity respectively to open files in mpv. I wish to do the same using fdfind and fzf.

As illustrated in this stackoverflow answer the following works and will be our starting point:

fdfind . /path/to/Music | fzf -m | xargs -d "\n" mpv

We can use the above command to select some files and play them using mpv. However we want a dialog to be created for our file selection. To do this, we create a bash script menu_fzf.sh with the following contents:

#!/bin/bash
urxvt -name fzf_menu -e bash -c "fzf -m $* < /proc/$$/fd/0 > /proc/$$/fd/1"

(urxvt is only an example and any other suitable terminal emulator can be used in the above).

Now if we run:

fdfind . /path/to/Music | menu_fzf.sh

then a new terminal opens that lets you pick multiple files from /path/to/Music with fzf and will return the selection as output to the calling terminal. So in fact right now we could run the following

fdfind . /path/to/Music | menu_fzf.sh | xargs -d "\n" mpv

and it will play the selected files in mpv. So far so good!

The tricky part now is how to do all the above using lua scripts from within mpv. By looking at the kdialog-open-files.lua or zenity-open-files.lua it seems I need to write something like this:

utils = require 'mp.utils'

-- TODO: make the following work so that file_select correctly stores
-- the files selected using menu_fzf.sh:
file_select = utils.subprocess({
    args = {'command', 'argument1', 'argument2', 'and so on'},
    cancellable = false,
    })

-- and then the rest of the code from kdialog-open-files.lua

However I am finding the official manual for utils.subprocess to be terse and inadequate. I have creatively tried picking command, argument1, argument2, and so on in the above, but nothing seems to work!

So I have two questions:

  1. Can you (please) complete the above code template into a working solution?
  2. Do you know where I can find more information on utils.subprocess? Is there a more detailed manual somewhere? Where is the source-code?

Remark: As I have never programmed in lua before I am struggling with solving question-1 above and will probably continue to struggle even if I had the source code for utils.subprocess. So really help with question-1 is of higher priority. But any help with question-2 is also great!

My failed attempts so far:

The following will append all files in current directory to playlist:

file_select = utils.subprocess({
  args = {'fdfind', '.', directory},
  cancellable = false,
})

The following fails:

file_select = utils.subprocess({
  args = {'fdfind', '.', directory, '|', 'fzf'},
  cancellable = false,
})

with error message:

[open_files] [fd error]: Search path '|' is not a directory.
[open_files] [fd error]: Search path 'fzf' is not a directory.

The following allows selecting multiple files from $HOME directory but (as expected) does not return the selection:

file_select = utils.subprocess({
  args = {'urxvt','-e','fzf', '-m'},
  cancellable = false,
})

The following doesn't work:

file_select = utils.subprocess({
  args = {'urxvt','-e','fzf', '-m', '>', '/proc/$$/fd/1'},
  cancellable = false,
})

The following opens up a new dialog with fzf but has nothing to select from:

file_select = utils.subprocess({
  args = {'menu_fzf.sh'},
  cancellable = false,
})

Solution

  • Create a bash script mpv_fzf_menu.sh with the following content:

    #!/bin/bash
    urxvt -name fzf_menu -e bash -c "fdfind . "$1" | fzf -m > /proc/$$/fd/1"
    

    Create a lua file open-files.lua with the following content:

    utils = require 'mp.utils'
    
    function select_files_dmenu_fzf()
        if mp.get_property("path") == nill then
            directory = ""
        else
            directory = utils.split_path(utils.join_path(mp.get_property("working-directory"), mp.get_property("path")))
        end
        
        file_select = utils.subprocess({
          args = {'mpv_fzf_menu.sh', directory},
          cancellable = false,
        })
    end
    
    function add_files()
       select_files_dmenu_fzf()
       if (file_select.status ~= 0) then return end
       
       local first_file = true
       for filename in string.gmatch(file_select.stdout, '[^\n]+') do
          mp.commandv('loadfile', filename, first_file and 'replace' or 'append')
          first_file = false
       end
    end
    
    mp.add_key_binding("Ctrl+f", "add-files-kdialog", add_files)
    

    (I am assuming you know what to do next: make the bash script executable and put it somewhere where bash finds it in its $PATH. Put the lua script in .config/mpv/scripts and everything should work, i.e. - when you press Ctrl+f within mpv a dialog should open to let you select files).

    Remark: All the functionalities of kdialog-open-files.lua have not been implemented. But the above lua script can be easily extended. You can further make things fancy by filtering for files with the specified extension with fdfind. And you can also use the bash script independent of the lua script. If you configure the --preview option of fzf then you can get a file preview before making selection.