mirror of https://github.com/mpv-player/mpv
select.lua: add this script
This adds script messages to select playlist entries, tracks, chapters, subtitle lines, bindings and properties using the newly introduced mp.input.select(). This fully closes #13964.
This commit is contained in:
parent
367a6b561a
commit
61f72bd512
|
@ -270,6 +270,46 @@ 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-s
|
||||
Select a subtitle track.
|
||||
|
||||
g-S
|
||||
Select a secondary subtitle track.
|
||||
|
||||
g-a
|
||||
Select an audio track.
|
||||
|
||||
g-v
|
||||
Select a video track.
|
||||
|
||||
g-t
|
||||
Select a track of any type.
|
||||
|
||||
g-c
|
||||
Select a chapter.
|
||||
|
||||
g-l
|
||||
Select a subtitle line to seek to. This currently requires ``ffmpeg`` in
|
||||
``PATH``, or in the same folder as mpv on Windows.
|
||||
|
||||
g-d
|
||||
Select an audio device.
|
||||
|
||||
g-b
|
||||
Select a defined input binding.
|
||||
|
||||
g-r
|
||||
Show the values of all properties.
|
||||
|
||||
(The following keys are valid if you have a keyboard with multimedia keys.)
|
||||
|
||||
PAUSE
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -168,6 +168,18 @@
|
|||
#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-s script-binding select/select-sid
|
||||
#g-S script-binding select/select-secondary-sid
|
||||
#g-a script-binding select/select-aid
|
||||
#g-v script-binding select/select-vid
|
||||
#g-t script-binding select/select-track
|
||||
#g-c script-binding select/select-chapter
|
||||
#g-l script-binding select/select-subtitle-line
|
||||
#g-d script-binding select/select-audio-device
|
||||
#g-b script-binding select/select-binding
|
||||
#g-r script-binding select/show-properties
|
||||
|
||||
#
|
||||
# Legacy bindings (may or may not be removed in the future)
|
||||
|
|
|
@ -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 --------------------
|
||||
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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}
|
||||
};
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -0,0 +1,352 @@
|
|||
--[[
|
||||
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 utils = require "mp.utils"
|
||||
local input = require "mp.input"
|
||||
|
||||
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-playlist", function ()
|
||||
local playlist = {}
|
||||
local default_item = 1
|
||||
|
||||
for i, entry in ipairs(mp.get_property_native("playlist")) do
|
||||
playlist[i] = select(2, utils.split_path(entry.filename))
|
||||
|
||||
if entry.playing then
|
||||
default_item = i
|
||||
end
|
||||
end
|
||||
|
||||
if #playlist == 0 then
|
||||
show_error("The playlist is empty.")
|
||||
return
|
||||
end
|
||||
|
||||
input.select({
|
||||
prompt = "Select a playlist entry:",
|
||||
items = playlist,
|
||||
default_item = default_item,
|
||||
submit = function (index)
|
||||
mp.commandv("playlist-play-index", index - 1)
|
||||
end,
|
||||
})
|
||||
end)
|
||||
|
||||
local function format_track(track)
|
||||
return (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["codec-profile"] and track.type == "audio"
|
||||
and track["codec-profile"] .. " " or "") ..
|
||||
(track["demux-samplerate"] and track["demux-samplerate"] / 1000 ..
|
||||
" kHz " or "") ..
|
||||
(track.external and "external " or "")
|
||||
):sub(1, -2) .. ")"
|
||||
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) .. ": " ..
|
||||
format_track(track)
|
||||
end
|
||||
|
||||
if #tracks == 0 then
|
||||
show_error("No available tracks.")
|
||||
return
|
||||
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
|
||||
end,
|
||||
})
|
||||
end)
|
||||
|
||||
local function select_track(property, type, prompt, error)
|
||||
local tracks = {}
|
||||
local items = {}
|
||||
local default_item = 1
|
||||
local track_id = mp.get_property_native(property)
|
||||
|
||||
for _, track in ipairs(mp.get_property_native("track-list")) do
|
||||
if track.type == type then
|
||||
tracks[#tracks + 1] = track
|
||||
items[#items + 1] = format_track(track)
|
||||
|
||||
if track.id == track_id then
|
||||
default_item = #items
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if #items == 0 then
|
||||
show_error(error)
|
||||
return
|
||||
end
|
||||
|
||||
input.select({
|
||||
prompt = prompt,
|
||||
items = items,
|
||||
default_item = default_item,
|
||||
submit = function (id)
|
||||
mp.set_property(property, tracks[id].selected and "no" or tracks[id].id)
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
mp.add_forced_key_binding(nil, "select-sid", function ()
|
||||
select_track("sid", "sub", "Select a subtitle:", "No available subtitles.")
|
||||
end)
|
||||
|
||||
mp.add_forced_key_binding(nil, "select-secondary-sid", function ()
|
||||
select_track("secondary-sid", "sub", "Select a secondary subtitle:",
|
||||
"No available subtitles.")
|
||||
end)
|
||||
|
||||
mp.add_forced_key_binding(nil, "select-aid", function ()
|
||||
select_track("aid", "audio", "Select an audio track:",
|
||||
"No available audio tracks.")
|
||||
end)
|
||||
|
||||
mp.add_forced_key_binding(nil, "select-vid", function ()
|
||||
select_track("vid", "video", "Select a video track:",
|
||||
"No available video tracks.")
|
||||
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 available chapters.")
|
||||
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)
|
||||
end,
|
||||
})
|
||||
end)
|
||||
|
||||
mp.add_forced_key_binding(nil, "select-subtitle-line", 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")
|
||||
end,
|
||||
})
|
||||
end)
|
||||
|
||||
mp.add_forced_key_binding(nil, "select-audio-device", function ()
|
||||
local devices = mp.get_property_native("audio-device-list")
|
||||
local items = {}
|
||||
-- This is only useful if an --audio-device has been explicitly set,
|
||||
-- otherwise its value is just auto and there is no current-audio-device
|
||||
-- property.
|
||||
local selected_device = mp.get_property("audio-device")
|
||||
local default_item = 1
|
||||
|
||||
if #devices == 0 then
|
||||
show_error("No available audio devices.")
|
||||
return
|
||||
end
|
||||
|
||||
for i, device in ipairs(devices) do
|
||||
items[i] = device.name .. " (" .. device.description .. ")"
|
||||
|
||||
if device.name == selected_device then
|
||||
default_item = i
|
||||
end
|
||||
end
|
||||
|
||||
input.select({
|
||||
prompt = "Select an audio device:",
|
||||
items = items,
|
||||
default_item = default_item,
|
||||
submit = function (id)
|
||||
mp.set_property("audio-device", devices[id].name)
|
||||
end,
|
||||
})
|
||||
end)
|
||||
|
||||
mp.add_forced_key_binding(nil, "select-binding", function ()
|
||||
local bindings = {}
|
||||
|
||||
for _, binding in pairs(mp.get_property_native("input-bindings")) do
|
||||
if binding.priority >= 0 and (
|
||||
bindings[binding.key] == nil or
|
||||
(bindings[binding.key].is_weak and not binding.is_weak) or
|
||||
(binding.is_weak == bindings[binding.key].is_weak and
|
||||
binding.priority > bindings[binding.key].priority)
|
||||
) then
|
||||
bindings[binding.key] = binding
|
||||
end
|
||||
end
|
||||
|
||||
local items = {}
|
||||
for _, binding in pairs(bindings) do
|
||||
if binding.cmd ~= "ignore" then
|
||||
items[#items + 1] = binding.key .. " " .. binding.cmd
|
||||
end
|
||||
end
|
||||
|
||||
table.sort(items)
|
||||
|
||||
input.select({
|
||||
prompt = "Select a binding:",
|
||||
items = items,
|
||||
submit = function (i)
|
||||
mp.command(items[i]:gsub("^.- ", ""))
|
||||
end,
|
||||
})
|
||||
end)
|
||||
|
||||
local properties = {}
|
||||
|
||||
local function add_property(property, value)
|
||||
value = value or mp.get_property_native(property)
|
||||
|
||||
if type(value) == "table" and next(value) then
|
||||
for key, val in pairs(value) do
|
||||
add_property(property .. "/" .. key, val)
|
||||
end
|
||||
else
|
||||
properties[#properties + 1] = property .. ": " .. utils.to_string(value)
|
||||
end
|
||||
end
|
||||
|
||||
mp.add_forced_key_binding(nil, "show-properties", function ()
|
||||
properties = {}
|
||||
|
||||
-- Don't log errors for renamed and removed properties.
|
||||
local msg_level_backup = mp.get_property("msg-level")
|
||||
mp.set_property("msg-level", msg_level_backup == "" and "cplayer=no"
|
||||
or msg_level_backup .. ",cplayer=no")
|
||||
|
||||
for _, property in pairs(mp.get_property_native("property-list")) do
|
||||
add_property(property)
|
||||
end
|
||||
|
||||
mp.set_property("msg-level", msg_level_backup)
|
||||
|
||||
add_property("current-tracks/audio")
|
||||
add_property("current-tracks/video")
|
||||
add_property("current-tracks/sub")
|
||||
add_property("current-tracks/sub2")
|
||||
|
||||
table.sort(properties)
|
||||
|
||||
input.select({
|
||||
prompt = "Inspect a property:",
|
||||
items = properties,
|
||||
submit = function (i)
|
||||
if mp.get_property_native("vo-configured") then
|
||||
mp.commandv("expand-properties", "show-text",
|
||||
(#properties[i] > 100 and
|
||||
"${osd-ass-cc/0}{\\fs9}${osd-ass-cc/1}" or "") ..
|
||||
"$>" .. properties[i], 20000)
|
||||
else
|
||||
mp.msg.info(properties[i])
|
||||
end
|
||||
end,
|
||||
})
|
||||
end)
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue