Skip to content

Commit

Permalink
select.lua: add this script
Browse files Browse the repository at this point in the history
This adds script messages to select playlist entries, tracks, chapters
and subtitle lines using the newly introduced mp.input.select().

This fully closes mpv-player#13964.
  • Loading branch information
guidocella committed May 8, 2024
1 parent 708e05e commit dadd968
Show file tree
Hide file tree
Showing 10 changed files with 250 additions and 2 deletions.
22 changes: 22 additions & 0 deletions DOCS/man/mpv.rst
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,28 @@ Alt+2 (and Command+2 on macOS)
Command + f (macOS only)
Toggle fullscreen (see also ``--fs``).

(The following keybindings open a selector in the console that lets you choose
from a list of items by typing part of the desired item and/or by navigating
them with keybindings: ``Down`` and ``Ctrl+n`` go down, ``Up`` and ``Ctrl+p`` go
up, ``Page down`` and ``Ctrl+f`` scroll down one page, and ``Page up`` and
``Ctrl+b`` scroll up one page.)

g-p
Select a playlist entry.

g-t
Select a track.

g-j
Select a secondary subtitle.

g-c
Select a chapter.

g-s
Select a subtitle line to seek to. This requires ``ffmpeg`` in PATH, or in
the same folder as mpv on Windows.

(The following keys are valid if you have a keyboard with multimedia keys.)

PAUSE
Expand Down
4 changes: 4 additions & 0 deletions DOCS/man/options.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1023,6 +1023,10 @@ Program Behavior
`Conditional auto profiles`_ for details. ``auto`` will load the script,
but immediately unload it if there are no conditional profiles.

``--load-select=<yes|no>``
Enable the builtin script that lets you select from lists of items (default:
yes). By default, its keybindings start with the ``g`` key.

``--player-operation-mode=<cplayer|pseudo-gui>``
For enabling "pseudo GUI mode", which means that the defaults for some
options are changed. This option should not normally be used directly, but
Expand Down
6 changes: 6 additions & 0 deletions etc/input.conf
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,12 @@
#ctrl+h cycle-values hwdec "auto-safe" "no" # toggle hardware decoding
#F8 show-text ${playlist} # show the playlist
#F9 show-text ${track-list} # show the list of video, audio and sub tracks
#g ignore
#g-p script-binding select/select-playlist
#g-t script-binding select/select-track
#g-j script-binding select/select-secondary-sub
#g-c script-binding select/select-chapter
#g-s script-binding select/sub-seek

#
# Legacy bindings (may or may not be removed in the future)
Expand Down
2 changes: 2 additions & 0 deletions options/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,7 @@ static const m_option_t mp_opts[] = {
{"load-auto-profiles",
OPT_CHOICE(lua_load_auto_profiles, {"no", 0}, {"yes", 1}, {"auto", -1}),
.flags = UPDATE_BUILTIN_SCRIPTS},
{"load-select", OPT_BOOL(lua_load_select), .flags = UPDATE_BUILTIN_SCRIPTS},
#endif

// ------------------------- stream options --------------------
Expand Down Expand Up @@ -969,6 +970,7 @@ static const struct MPOpts mp_default_opts = {
.lua_load_stats = true,
.lua_load_console = true,
.lua_load_auto_profiles = -1,
.lua_load_select = true,
#endif
.auto_load_scripts = true,
.loop_times = 1,
Expand Down
1 change: 1 addition & 0 deletions options/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ typedef struct MPOpts {
bool lua_load_stats;
bool lua_load_console;
int lua_load_auto_profiles;
bool lua_load_select;

bool auto_load_scripts;

Expand Down
2 changes: 1 addition & 1 deletion player/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,7 @@ typedef struct MPContext {

struct mp_ipc_ctx *ipc_ctx;

int64_t builtin_script_ids[5];
int64_t builtin_script_ids[6];

mp_mutex abort_lock;

Expand Down
3 changes: 3 additions & 0 deletions player/lua.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ static const char * const builtin_lua_scripts[][2] = {
},
{"@auto_profiles.lua",
# include "player/lua/auto_profiles.lua.inc"
},
{"@select.lua",
# include "player/lua/select.lua.inc"
},
{0}
};
Expand Down
2 changes: 1 addition & 1 deletion player/lua/meson.build
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
lua_files = ['defaults.lua', 'assdraw.lua', 'options.lua', 'osc.lua',
'ytdl_hook.lua', 'stats.lua', 'console.lua', 'auto_profiles.lua',
'input.lua', 'fzy.lua']
'input.lua', 'fzy.lua', 'select.lua']
foreach file: lua_files
lua_file = custom_target(file,
input: join_paths(source_root, 'player', 'lua', file),
Expand Down
209 changes: 209 additions & 0 deletions player/lua/select.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
--[[
This file is part of mpv.
mpv is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
mpv 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with mpv. If not, see <http://www.gnu.org/licenses/>.
]]

local input = require "mp.input"

mp.add_forced_key_binding(nil, "select-playlist", function ()
local playlist = {}
local default_item = 1

for i, entry in ipairs(mp.get_property_native("playlist")) do
playlist[i] = select(2, require "mp.utils".split_path(entry.filename))

if entry.playing then
default_item = i
end
end

input.select({
prompt = "Select a playlist entry:",
items = playlist,
default_item = default_item,
submit = function (index)
mp.commandv("playlist-play-index", index - 1)
input.terminate()
end,
})
end)

mp.add_forced_key_binding(nil, "select-track", function ()
local tracks = {}

for i, track in ipairs(mp.get_property_native("track-list")) do
tracks[i] = track.type:sub(1, 1):upper() .. track.type:sub(2) .. ": " ..
(track.selected and "" or "") ..
(track.title and " " .. track.title or "") ..
" (" .. (
(track.lang and track.lang .. " " or "") ..
(track.codec and track.codec .. " " or "") ..
(track["demux-w"] and track["demux-w"] .. "x" .. track["demux-h"] .. " " or "") ..
(track["demux-fps"] and not track.image and string.format("%.3f", track["demux-fps"]) .. "FPS " or "") ..
(track["demux-channel-count"] and track["demux-channel-count"] .. "ch " or "") ..
(track["demux-samplerate"] and track["demux-samplerate"] / 1000 .. "kHz " or "") ..
(track.external and "external " or "")
):sub(1, -2) .. ")"
end

input.select({
prompt = "Select a track:",
items = tracks,
submit = function (id)
local track = mp.get_property_native("track-list/" .. id - 1)
if track then
mp.set_property(track.type, track.selected and "no" or track.id)
end
input.terminate()
end,
})
end)

local function show_error(message)
mp.msg.error(message)
if mp.get_property_native("vo-configured") then
mp.osd_message(message)
end
end

mp.add_forced_key_binding(nil, "select-secondary-sub", function ()
local subs = {}
local sub_indexes = {}
local default_item = 1
local secondary_sid = mp.get_property_native("secondary-sid")

for i, track in ipairs(mp.get_property_native("track-list")) do
if track.type == "sub" then
subs[#subs + 1] = (track.selected and "" or "") ..
(track.title and " " .. track.title or "") ..
" (" .. (
(track.lang and track.lang .. " " or "") ..
(track.codec and track.codec .. " " or "") ..
(track.external and "external " or "")
):sub(1, -2) .. ")"

sub_indexes[#sub_indexes + 1] = i - 1

if track.id == secondary_sid then
default_item = #subs
end
end
end

if #subs == 0 then
show_error("No subtitle is loaded.")
return
end

input.select({
prompt = "Select a secondary subtitle:",
items = subs,
default_item = default_item,
submit = function (id)
local sub = mp.get_property_native("track-list/" .. sub_indexes[id])
if sub then
mp.set_property("secondary-sid", sub.selected and "no" or sub.id)
end
input.terminate()
end,
})
end)

local function format_time(t)
local h = math.floor(t / (60 * 60))
t = t - (h * 60 * 60)
local m = math.floor(t / 60)
local s = t - (m * 60)

return string.format("%.2d:%.2d:%.2d", h, m, s)
end

mp.add_forced_key_binding(nil, "select-chapter", function ()
local chapters = {}
local default_item = mp.get_property_native("chapter")

if default_item == nil then
show_error("No chapters are present.")
return
end

for i, chapter in ipairs(mp.get_property_native("chapter-list")) do
chapters[i] = format_time(chapter.time) .. " " .. chapter.title
end

input.select({
prompt = "Select a chapter:",
items = chapters,
default_item = default_item + 1,
submit = function (chapter)
mp.set_property("chapter", chapter - 1)
input.terminate()
end,
})
end)

mp.add_forced_key_binding(nil, "sub-seek", function ()
local sub = mp.get_property_native("current-tracks/sub")

if sub == nil then
show_error("No subtitle is loaded.")
return
end

local r = mp.command_native({
name = "subprocess",
capture_stdout = true,
args = sub.external
and {"ffmpeg", "-loglevel", "quiet", "-i", sub["external-filename"], "-f", "lrc", "-map_metadata", "-1", "-fflags", "+bitexact", "-"}
or {"ffmpeg", "-loglevel", "quiet", "-i", mp.get_property("path"), "-map", "s:" .. sub["id"] - 1, "-f", "lrc", "-map_metadata", "-1", "-fflags", "+bitexact", "-"}
})

if r.status < 0 then
show_error("subprocess error: " .. r.error_string)
return
end

if r.status > 0 then
show_error("ffmpeg failed with code " .. r.status)
return
end

local sub_lines = {}
local default_item = 1

local sub_start = mp.get_property_native("sub-start", 0)
local m = math.floor(sub_start / 60)
local s = sub_start - m * 60
sub_start = string.format("%.2d:%05.2f", m, s)

-- Strip HTML and ASS tags.
for line in r.stdout:gsub("<.->", ""):gsub("{\\.-}", ""):gmatch("[^\n]+") do
sub_lines[#sub_lines + 1] = line

if line:find("^%[" .. sub_start) then
default_item = #sub_lines
end
end

input.select({
prompt = "Select a line to seek to:",
items = sub_lines,
default_item = default_item,
submit = function (index)
mp.commandv("seek", sub_lines[index]:match("[%d:%.]+"), "absolute")
input.terminate()
end,
})
end)
1 change: 1 addition & 0 deletions player/scripting.c
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ void mp_load_builtin_scripts(struct MPContext *mpctx)
load_builtin_script(mpctx, 3, mpctx->opts->lua_load_console, "@console.lua");
load_builtin_script(mpctx, 4, mpctx->opts->lua_load_auto_profiles,
"@auto_profiles.lua");
load_builtin_script(mpctx, 5, mpctx->opts->lua_load_select, "@select.lua");
}

bool mp_load_scripts(struct MPContext *mpctx)
Expand Down

0 comments on commit dadd968

Please sign in to comment.