Add file navigation commands
This commit is contained in:
48
README.md
48
README.md
@@ -94,6 +94,10 @@ color_background_text=ffffff
|
||||
autohide=no
|
||||
# display window title (filename) in top window controls bar in no-border mode
|
||||
title=no
|
||||
# load first file when calling next on last file in a directory and vice versa
|
||||
directory_navigation_loops=no
|
||||
# file types to display in file explorer when navigating media files
|
||||
media_types=3gp,avi,bmp,flac,flv,gif,h264,h265,jpeg,jpg,m4a,m4v,mid,midi,mkv,mov,mp3,mp4,mp4a,mp4v,mpeg,mpg,oga,ogg,ogm,ogv,opus,png,rmvb,svg,tif,tiff,wav,weba,webm,webp,wma,wmv
|
||||
# file types to display in file explorer when loading external subtitles
|
||||
subtitle_types=aqt,gsub,jss,sub,ttxt,pjs,psb,rt,smi,slt,ssf,srt,ssa,ass,usf,idx,vt
|
||||
# used to approximate text width
|
||||
@@ -205,9 +209,39 @@ Menu to select an item from playlist.
|
||||
|
||||
Show current file in your operating systems' file explorer.
|
||||
|
||||
#### `navigate-directory`
|
||||
|
||||
Menu to navigate media files in current files' directory with current file preselected.
|
||||
|
||||
#### `next-file`
|
||||
|
||||
Open next file in current directory. Set `directory_navigation_loops=yes` to open first file when at the end.
|
||||
|
||||
#### `prev-file`
|
||||
|
||||
Open previous file in current directory. Set `directory_navigation_loops=yes` to open last file when at the start.
|
||||
|
||||
#### `first-file`
|
||||
|
||||
Open first file in current directory.
|
||||
|
||||
#### `last-file`
|
||||
|
||||
Open last file in current directory.
|
||||
|
||||
#### `delete-file-next`
|
||||
|
||||
Delete currently playing file and start next file in playlist (if there is a playlist) or current directory.
|
||||
|
||||
Useful when watching periodic content.
|
||||
|
||||
#### `delete-file-quit`
|
||||
|
||||
Delete currently playing file and quit mpv.
|
||||
|
||||
## Context menu
|
||||
|
||||
**uosc** provides a way to build, display, and use your own context menu. Limitation is that the UI rendering API provided by mpv can only render stuff within window borders, so the menu can't float above it but needs to fit it. This might be annoying for tiny videos but otherwise it accomplishes the same thing.
|
||||
**uosc** provides a way to build, display, and use your own context menu. Limitation is that the UI rendering API provided by mpv can only render stuff within window borders, so the menu can't float above it but needs to fit inside. This might be annoying for tiny videos but otherwise it accomplishes the same thing.
|
||||
|
||||
To display the menu, add **uosc**'s `context-menu` command to a key of your choice. Example to bind it to **right click** and **menu** buttons:
|
||||
|
||||
@@ -220,11 +254,11 @@ menu script-binding uosc/context-menu
|
||||
|
||||
### Adding items to menu
|
||||
|
||||
Adding items to menu is facilitated by same line commenting of your keybinds in `input.conf` with special comment syntax. **uosc** will than parse this file and build the context menu out of it.
|
||||
Adding items to menu is facilitated by commenting of your keybinds in `input.conf` with special comment syntax. **uosc** will than parse this file and build the context menu out of it.
|
||||
|
||||
#### Syntax
|
||||
|
||||
Comment has to be at the end of the line with key-command binding.
|
||||
Comment has to be at the end of the line with the binding.
|
||||
|
||||
Comment has to start with `#!`.
|
||||
|
||||
@@ -234,9 +268,9 @@ Title can be split with `>` to define nested menus. There is no limit on nesting
|
||||
|
||||
Use `#` instead of a key if you don't necessarily want to bind a key to a command, but still want it in the menu.
|
||||
|
||||
If multiple menu items with the same command are defined, **uosc** will concatenate them into one item and just display all available shortcuts as that items' hint, while using the title of the first item that added the command to the menu.
|
||||
If multiple menu items with the same command are defined, **uosc** will concatenate them into one item and just display all available shortcuts as that items' hint, while using the title of the first defined item.
|
||||
|
||||
Menu items are displayed in the order they were defined in `input.conf` file.
|
||||
Menu items are displayed in the order they are defined in `input.conf` file.
|
||||
|
||||
#### Examples
|
||||
|
||||
@@ -252,7 +286,7 @@ Adds a stay-on-top toggle with no keybind:
|
||||
# cycle ontop #! Toggle on-top
|
||||
```
|
||||
|
||||
Defines multiple shortcuts to display in the first items' hint (items with same command get concatenated):
|
||||
Define and display multiple shortcuts in single items' menu hint (items with same command get concatenated):
|
||||
|
||||
```
|
||||
esc quit #! Quit
|
||||
@@ -268,6 +302,8 @@ Adds an **Aspect ratio** submenu with multiple items that have no keybinds defin
|
||||
# set video-aspect-override "2.35:1" #! Aspect ratio > 2.35:1
|
||||
```
|
||||
|
||||
To see all the commands ou can bind keys or menu items to, refer to [mpv's list of input commands documentation](https://mpv.io/manual/master/#list-of-input-commands).
|
||||
|
||||
## Tips
|
||||
|
||||
If the UI feels sluggish/slow to you, it's because when video is playing, the UI rendering frequency is chained to its frame rate, so unless you are the type of person that can't see above 24fps, it does feel sluggish.
|
||||
|
@@ -62,6 +62,10 @@ color_background_text=ffffff
|
||||
autohide=no
|
||||
# display window title (filename) in top window controls bar in no-border mode
|
||||
title=no
|
||||
# load first file when calling next on last file in a directory and vice versa
|
||||
directory_navigation_loops=no
|
||||
# file types to display in file explorer when navigating media files
|
||||
media_types=3gp,avi,bmp,flac,flv,gif,h264,h265,jpeg,jpg,m4a,m4v,mid,midi,mkv,mov,mp3,mp4,mp4a,mp4v,mpeg,mpg,oga,ogg,ogm,ogv,opus,png,rmvb,svg,tif,tiff,wav,weba,webm,webp,wma,wmv
|
||||
# file types to display in file explorer when loading external subtitles
|
||||
subtitle_types=aqt,gsub,jss,sub,ttxt,pjs,psb,rt,smi,slt,ssf,srt,ssa,ass,usf,idx,vt
|
||||
# used to approximate text width
|
||||
|
256
uosc.lua
256
uosc.lua
@@ -80,6 +80,10 @@ color_background_text=ffffff
|
||||
autohide=no
|
||||
# display window title (filename) in top window controls bar in no-border mode
|
||||
title=no
|
||||
# load first file when calling next on last file in a directory and vice versa
|
||||
directory_navigation_loops=no
|
||||
# file types to display in file explorer when navigating media files
|
||||
media_types=3gp,avi,bmp,flac,flv,gif,h264,h265,jpeg,jpg,m4a,m4v,mid,midi,mkv,mov,mp3,mp4,mp4a,mp4v,mpeg,mpg,oga,ogg,ogm,ogv,opus,png,rmvb,svg,tif,tiff,wav,weba,webm,webp,wma,wmv
|
||||
# file types to display in file explorer when loading external subtitles
|
||||
subtitle_types=aqt,gsub,jss,sub,ttxt,pjs,psb,rt,smi,slt,ssf,srt,ssa,ass,usf,idx,vt
|
||||
# used to approximate text width
|
||||
@@ -143,6 +147,13 @@ Key script-binding uosc/select-audio
|
||||
Key script-binding uosc/select-video
|
||||
Key script-binding uosc/navigate-playlist
|
||||
Key script-binding uosc/show-in-directory
|
||||
Key script-binding uosc/navigate-directory
|
||||
Key script-binding uosc/next-file
|
||||
Key script-binding uosc/prev-file
|
||||
Key script-binding uosc/first-file
|
||||
Key script-binding uosc/last-file
|
||||
Key script-binding uosc/delete-file-next
|
||||
Key script-binding uosc/delete-file-quit
|
||||
```
|
||||
]]
|
||||
|
||||
@@ -192,6 +203,8 @@ local options = {
|
||||
color_background_text = 'ffffff',
|
||||
autohide = false,
|
||||
title = false,
|
||||
directory_navigation_loops = false,
|
||||
media_types = '3gp,avi,bmp,flac,flv,gif,h264,h265,jpeg,jpg,m4a,m4v,mid,midi,mkv,mov,mp3,mp4,mp4a,mp4v,mpeg,mpg,oga,ogg,ogm,ogv,opus,png,rmvb,svg,tif,tiff,wav,weba,webm,webp,wma,wmv',
|
||||
subtitle_types = 'aqt,gsub,jss,sub,ttxt,pjs,psb,rt,smi,slt,ssf,srt,ssa,ass,usf,idx,vt',
|
||||
font_height_to_letter_width_ratio = 0.5,
|
||||
chapter_ranges = '',
|
||||
@@ -269,36 +282,36 @@ function split(str, pattern)
|
||||
local start_index, end_index, capture = str:find(full_pattern, 1)
|
||||
while start_index do
|
||||
if start_index ~= 1 or capture ~= '' then
|
||||
table.insert(list, capture)
|
||||
list[#list +1] = capture
|
||||
end
|
||||
last_end = end_index + 1
|
||||
start_index, end_index, capture = str:find(full_pattern, last_end)
|
||||
end
|
||||
if last_end <= #str then
|
||||
capture = str:sub(last_end)
|
||||
table.insert(list, capture)
|
||||
list[#list +1] = capture
|
||||
end
|
||||
return list
|
||||
end
|
||||
|
||||
function itable_find(haystack, needle)
|
||||
local is_needle = type(needle) == 'function' and needle or function(value)
|
||||
local is_needle = type(needle) == 'function' and needle or function(index, value)
|
||||
return value == needle
|
||||
end
|
||||
for index, value in ipairs(haystack) do
|
||||
if is_needle(value) then return index end
|
||||
if is_needle(index, value) then return index, value end
|
||||
end
|
||||
end
|
||||
|
||||
function itable_filter(haystack, needle)
|
||||
local is_needle = type(needle) == 'function' and needle or function(value)
|
||||
local is_needle = type(needle) == 'function' and needle or function(index, value)
|
||||
return value == needle
|
||||
end
|
||||
local results = {}
|
||||
local filtered = {}
|
||||
for index, value in ipairs(haystack) do
|
||||
if is_needle(value) then results[#results] = value end
|
||||
if is_needle(index, value) then filtered[#filtered + 1] = value end
|
||||
end
|
||||
return results
|
||||
return filtered
|
||||
end
|
||||
|
||||
function itable_remove(haystack, needle)
|
||||
@@ -308,7 +321,7 @@ function itable_remove(haystack, needle)
|
||||
local new_table = {}
|
||||
for _, value in ipairs(haystack) do
|
||||
if not should_remove(value) then
|
||||
table.insert(new_table, value)
|
||||
new_table[#new_table + 1] = value
|
||||
end
|
||||
end
|
||||
return new_table
|
||||
@@ -318,12 +331,13 @@ function itable_slice(haystack, start_pos, end_pos)
|
||||
start_pos = start_pos and start_pos or 1
|
||||
end_pos = end_pos and end_pos or #haystack
|
||||
|
||||
if end_pos < 0 then end_pos = #haystack - end_pos end
|
||||
if end_pos < 0 then end_pos = #haystack + end_pos + 1 end
|
||||
if start_pos < 0 then start_pos = #haystack + start_pos + 1 end
|
||||
|
||||
local new_table = {}
|
||||
for index, value in ipairs(haystack) do
|
||||
if index >= start_pos and index <= end_pos then
|
||||
table.insert(new_table, value)
|
||||
new_table[#new_table + 1] = value
|
||||
end
|
||||
end
|
||||
return new_table
|
||||
@@ -430,6 +444,11 @@ function is_protocol(path)
|
||||
return path:match('^%a[%a%d-_]+://')
|
||||
end
|
||||
|
||||
function get_extension(path)
|
||||
local parts = split(path, '%.')
|
||||
return parts and parts[#parts] or nil
|
||||
end
|
||||
|
||||
-- Serializes path into its semantic parts
|
||||
function serialize_path(path)
|
||||
path = ensure_absolute_path(path)
|
||||
@@ -446,6 +465,50 @@ function serialize_path(path)
|
||||
}
|
||||
end
|
||||
|
||||
function get_files_in_directory(directory, allowed_types)
|
||||
local files, error = utils.readdir(directory, 'files')
|
||||
|
||||
if not files then
|
||||
msg.error('Retrieving files failed: '..(error or ''))
|
||||
return
|
||||
end
|
||||
|
||||
-- Filter only requested file types
|
||||
if allowed_types then
|
||||
files = itable_filter(files, function(_, file)
|
||||
local extension = get_extension(file)
|
||||
return extension and itable_find(allowed_types, extension:lower())
|
||||
end)
|
||||
end
|
||||
|
||||
table.sort(files)
|
||||
|
||||
return files
|
||||
end
|
||||
|
||||
function get_adjacent_media_file(file_path, direction)
|
||||
local current_file = serialize_path(file_path)
|
||||
|
||||
local files = get_files_in_directory(current_file.dirname, options.media_types)
|
||||
|
||||
if not files then return end
|
||||
|
||||
for index, file in ipairs(files) do
|
||||
if current_file.basename == file then
|
||||
if direction == 'forward' then
|
||||
if files[index + 1] then return files[index + 1] end
|
||||
if options.directory_navigation_loops and files[1] then return files[1] end
|
||||
else
|
||||
if files[index - 1] then return files[index - 1] end
|
||||
if options.directory_navigation_loops and files[#files] then return files[#files] end
|
||||
end
|
||||
|
||||
-- This is the only file in directory
|
||||
return nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Element
|
||||
--[[
|
||||
Signature:
|
||||
@@ -505,7 +568,7 @@ function Elements:add(name, element)
|
||||
|
||||
-- Replace if element already exists
|
||||
if self:has(name) then
|
||||
insert_index = itable_find(Elements.itable, function(element)
|
||||
insert_index = itable_find(Elements.itable, function(_, element)
|
||||
return element.name == name
|
||||
end)
|
||||
end
|
||||
@@ -602,7 +665,7 @@ function Menu:open(items, open_item, opts)
|
||||
|
||||
-- Preselect first 'item.selected == true' item
|
||||
if not opts.selected_item then
|
||||
local preselected_item = itable_find(items, function(item) return not not item.selected end)
|
||||
local preselected_item = itable_find(items, function(_, item) return not not item.selected end)
|
||||
if preselected_item then
|
||||
this.selected_item = preselected_item
|
||||
end
|
||||
@@ -614,6 +677,9 @@ function Menu:open(items, open_item, opts)
|
||||
-- Set initial dimensions
|
||||
this:on_display_resize()
|
||||
|
||||
-- Scroll to selected item
|
||||
this:center_selected_item()
|
||||
|
||||
-- Transition in animation
|
||||
menu.transition = {to = 'child', target = this}
|
||||
local start_offset = this.parent_menu and (this.parent_menu.width + this.width) / 2 or 0
|
||||
@@ -659,6 +725,11 @@ function Menu:open(items, open_item, opts)
|
||||
this.scroll_y = math.max(math.min(pos, this.scroll_height), 0)
|
||||
request_render()
|
||||
end,
|
||||
center_selected_item = function(this)
|
||||
if this.selected_item then
|
||||
this:scroll_to(round((this.scroll_step * (this.selected_item - 1)) - ((this.height - this.scroll_step) / 2)))
|
||||
end
|
||||
end,
|
||||
on_wheel_up = function(this)
|
||||
this:scroll_to(this.scroll_y - this.scroll_step)
|
||||
this:on_global_mouse_move()
|
||||
@@ -714,11 +785,6 @@ function Menu:open(items, open_item, opts)
|
||||
close = function()
|
||||
menu:close()
|
||||
end,
|
||||
center_selected_item = function(this)
|
||||
if this.selected_item then
|
||||
this:scroll_to(round((this.scroll_step * (this.selected_item - 1)) - ((this.height - this.scroll_step) / 2)))
|
||||
end
|
||||
end,
|
||||
open_selected_item = function(this)
|
||||
-- If there is a transition active and this method got called, it
|
||||
-- means we are animating from this menu to parent menu, and all
|
||||
@@ -1835,7 +1901,7 @@ for _, definition in ipairs(split(options.chapter_ranges, ' *,+ *')) do
|
||||
|
||||
function end_range(chapter)
|
||||
current_range['end'] = chapter
|
||||
table.insert(chapter_range.ranges, current_range)
|
||||
chapter_range.ranges[#chapter_range.ranges + 1] = current_range
|
||||
-- Mark both chapter objects
|
||||
current_range['start']._uosc_used_as_range_point = true
|
||||
current_range['end']._uosc_used_as_range_point = true
|
||||
@@ -1884,7 +1950,7 @@ for _, definition in ipairs(split(options.chapter_ranges, ' *,+ *')) do
|
||||
end
|
||||
|
||||
state.chapter_ranges = state.chapter_ranges or {}
|
||||
table.insert(state.chapter_ranges, chapter_range)
|
||||
state.chapter_ranges[#state.chapter_ranges + 1] = chapter_range
|
||||
|
||||
::continue::
|
||||
end
|
||||
@@ -1932,7 +1998,7 @@ state.context_menu_items = (function()
|
||||
|
||||
if not submenus_by_id[submenu_id] then
|
||||
submenus_by_id[submenu_id] = {title = title_part, items = {}}
|
||||
table.insert(target_menu, submenus_by_id[submenu_id])
|
||||
target_menu[#target_menu + 1] = submenus_by_id[submenu_id]
|
||||
end
|
||||
|
||||
target_menu = submenus_by_id[submenu_id].items
|
||||
@@ -1946,7 +2012,7 @@ state.context_menu_items = (function()
|
||||
hint = not is_dummy and key or nil,
|
||||
value = command
|
||||
}
|
||||
table.insert(target_menu, items_by_command[command])
|
||||
target_menu[#target_menu + 1] = items_by_command[command]
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1958,6 +2024,14 @@ end)()
|
||||
|
||||
-- EVENT HANDLERS
|
||||
|
||||
function create_state_setter(name)
|
||||
return function(_, value)
|
||||
state[name] = value
|
||||
dispatch_event_to_elements('prop_'..name, value)
|
||||
request_render()
|
||||
end
|
||||
end
|
||||
|
||||
function dispatch_event_to_elements(name, ...)
|
||||
for _, element in pairs(elements) do
|
||||
if element.proximity_raw == 0 then
|
||||
@@ -1981,8 +2055,7 @@ function handle_mouse_leave()
|
||||
end
|
||||
|
||||
function create_mouse_event_handler(source)
|
||||
if source == 'mouse_move'
|
||||
then
|
||||
if source == 'mouse_move' then
|
||||
return function()
|
||||
if cursor.hidden then
|
||||
tween_element_stop(state)
|
||||
@@ -2008,11 +2081,35 @@ function create_mouse_event_handler(source)
|
||||
end
|
||||
end
|
||||
|
||||
function state_setter(name)
|
||||
return function(_, value)
|
||||
state[name] = value
|
||||
dispatch_event_to_elements('prop_'..name, value)
|
||||
request_render()
|
||||
function create_directory_navigator(direction)
|
||||
return function()
|
||||
local path = mp.get_property_native("path")
|
||||
|
||||
if is_protocol(path) then return end
|
||||
|
||||
local next_file = get_adjacent_media_file(path, direction)
|
||||
|
||||
if next_file then
|
||||
mp.commandv("loadfile", next_file)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function create_adjacent_media_file_index_selector(index)
|
||||
return function()
|
||||
local path = mp.get_property_native("path")
|
||||
|
||||
if is_protocol(path) then return end
|
||||
|
||||
local dirname = serialize_path(path).dirname
|
||||
local files, error = get_files_in_directory(dirname, options.media_types)
|
||||
|
||||
if not files then return end
|
||||
if index < 0 then index = #files + index + 1 end
|
||||
|
||||
if files[index] then
|
||||
mp.commandv("loadfile", utils.join_path(dirname, files[index]))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2062,37 +2159,36 @@ function create_select_tracklist_type_menu_opener(menu_title, track_type, track_
|
||||
end
|
||||
end
|
||||
|
||||
function open_load_subtitles_menu(directory)
|
||||
function open_file_navigation_menu(directory, handle_select, allowed_types, selected_file)
|
||||
directory = serialize_path(directory)
|
||||
local directories, error = utils.readdir(directory.path, 'dirs')
|
||||
local files, error = utils.readdir(directory.path, 'files')
|
||||
local files, error = get_files_in_directory(directory.path, allowed_types)
|
||||
|
||||
if not files or not directories then
|
||||
msg.error('Retrieving files from '..directory..' failed: '..(error or ''))
|
||||
return
|
||||
end
|
||||
|
||||
-- List is not sorted on linux
|
||||
if state.os ~= 'linux' then
|
||||
table.sort(directories)
|
||||
table.sort(files)
|
||||
end
|
||||
table.sort(directories)
|
||||
table.sort(files)
|
||||
|
||||
local subtitle_types = split(options.subtitle_types, '[, ]+')
|
||||
-- Pre-populate items with parent directory selector if not at root
|
||||
local items = not directory.dirname and {} or {
|
||||
{title = '..', hint = 'parent dir', value = directory.dirname}
|
||||
}
|
||||
|
||||
for _, dir in ipairs(directories) do
|
||||
local serialized = serialize_path(utils.join_path(directory.path, dir))
|
||||
table.insert(items, {title = serialized.basename, value = serialized.path, hint = '/'})
|
||||
items[#items + 1] = {title = serialized.basename, value = serialized.path, hint = '/'}
|
||||
end
|
||||
|
||||
for _, file in ipairs(files) do
|
||||
local serialized = serialize_path(utils.join_path(directory.path, file))
|
||||
if itable_find(subtitle_types, serialized.extension:lower()) then
|
||||
table.insert(items, {title = serialized.basename, value = serialized.path})
|
||||
end
|
||||
items[#items + 1] = {
|
||||
title = serialized.basename,
|
||||
value = serialized.path,
|
||||
selected = selected_file == file
|
||||
}
|
||||
end
|
||||
|
||||
menu:open(items, function(path)
|
||||
@@ -2104,14 +2200,19 @@ function open_load_subtitles_menu(directory)
|
||||
end
|
||||
|
||||
if meta.is_dir then
|
||||
open_load_subtitles_menu(path)
|
||||
open_file_navigation_menu(path, handle_select, allowed_types)
|
||||
else
|
||||
mp.commandv('sub-add', path)
|
||||
handle_select(path)
|
||||
menu:close()
|
||||
end
|
||||
end, {title = directory.basename..'/', title_height = 36})
|
||||
end, {title = directory.basename..'/', title_height = 36, select_on_hover = false})
|
||||
end
|
||||
|
||||
-- VALUE SERIALIZATION/NORMALIZATION
|
||||
|
||||
options.media_types = split(options.media_types, ' *, *')
|
||||
options.subtitle_types = split(options.subtitle_types, ' *, *')
|
||||
|
||||
-- HOOKS
|
||||
|
||||
mp.register_event('file-loaded', function()
|
||||
@@ -2120,18 +2221,18 @@ mp.register_event('file-loaded', function()
|
||||
end)
|
||||
|
||||
mp.observe_property('chapter-list', 'native', parse_chapters)
|
||||
mp.observe_property('fullscreen', 'bool', state_setter('fullscreen'))
|
||||
mp.observe_property('window-maximized', 'bool', state_setter('maximized'))
|
||||
mp.observe_property('idle-active', 'bool', state_setter('idle'))
|
||||
mp.observe_property('pause', 'bool', state_setter('paused'))
|
||||
mp.observe_property('fullscreen', 'bool', create_state_setter('fullscreen'))
|
||||
mp.observe_property('window-maximized', 'bool', create_state_setter('maximized'))
|
||||
mp.observe_property('idle-active', 'bool', create_state_setter('idle'))
|
||||
mp.observe_property('pause', 'bool', create_state_setter('paused'))
|
||||
mp.observe_property('volume', 'number', function(_, value)
|
||||
local is_initial_call = state.volume == nil
|
||||
state.volume = value
|
||||
if not is_initial_call then elements.volume.flash() end
|
||||
request_render()
|
||||
end)
|
||||
mp.observe_property('volume-max', 'number', state_setter('volume_max'))
|
||||
mp.observe_property('mute', 'bool', state_setter('mute'))
|
||||
mp.observe_property('volume-max', 'number', create_state_setter('volume_max'))
|
||||
mp.observe_property('mute', 'bool', create_state_setter('mute'))
|
||||
mp.observe_property('border', 'bool', function (_, border)
|
||||
state.border = border
|
||||
-- Sets 1px bottom border for bars in no-border mode
|
||||
@@ -2163,14 +2264,14 @@ local base_keybinds = {
|
||||
{'mouse_leave', create_mouse_event_handler('mouse_leave')},
|
||||
}
|
||||
if options.pause_on_click then
|
||||
table.insert(base_keybinds, {'mbtn_left', function()
|
||||
base_keybinds[#base_keybinds + 1] = {'mbtn_left', function()
|
||||
if mp.get_time() - state.last_base_mbtn_left_down_time < 0.11 then
|
||||
mp.command('cycle pause')
|
||||
end
|
||||
end, function()
|
||||
state.last_base_mbtn_left_down_time = mp.get_time()
|
||||
end
|
||||
})
|
||||
}
|
||||
end
|
||||
mp.set_key_bindings(base_keybinds, 'mouse_movement', 'force')
|
||||
mp.enable_key_bindings('mouse_movement', 'allow-vo-dragging+allow-hide-cursor')
|
||||
@@ -2201,7 +2302,14 @@ mp.add_key_binding(nil, 'context-menu', function()
|
||||
end
|
||||
end)
|
||||
mp.add_key_binding(nil, 'load-subtitles', function()
|
||||
open_load_subtitles_menu(serialize_path(mp.get_property_native('path')).dirname)
|
||||
local path = mp.get_property_native('path')
|
||||
if not is_protocol(path) then
|
||||
open_file_navigation_menu(
|
||||
serialize_path(path).dirname,
|
||||
function(path) mp.commandv('sub-add', path) end,
|
||||
options.subtitle_types
|
||||
)
|
||||
end
|
||||
end)
|
||||
mp.add_key_binding(nil, 'select-subtitles', create_select_tracklist_type_menu_opener('Subtitles', 'sub', 'sid'))
|
||||
mp.add_key_binding(nil, 'select-audio', create_select_tracklist_type_menu_opener('Audio', 'audio', 'aid'))
|
||||
@@ -2246,3 +2354,45 @@ mp.add_key_binding(nil, 'show-in-directory', function()
|
||||
end
|
||||
end
|
||||
end)
|
||||
mp.add_key_binding(nil, 'navigate-directory', function()
|
||||
local path = mp.get_property_native('path')
|
||||
if not is_protocol(path) then
|
||||
path = serialize_path(path)
|
||||
open_file_navigation_menu(
|
||||
path.dirname,
|
||||
function(path) mp.commandv('loadfile', path) end,
|
||||
options.media_types,
|
||||
path.basename
|
||||
)
|
||||
end
|
||||
end)
|
||||
mp.add_key_binding(nil, 'next-file', create_directory_navigator('forward'))
|
||||
mp.add_key_binding(nil, 'prev-file', create_directory_navigator('backward'))
|
||||
mp.add_key_binding(nil, 'first-file', create_adjacent_media_file_index_selector(1))
|
||||
mp.add_key_binding(nil, 'last-file', create_adjacent_media_file_index_selector(-1))
|
||||
mp.add_key_binding(nil, 'delete-file-next', function()
|
||||
local path = mp.get_property_native('path')
|
||||
|
||||
if is_protocol(path) then return end
|
||||
|
||||
local playlist_count = mp.get_property_native('playlist-count')
|
||||
|
||||
if playlist_count > 1 then
|
||||
mp.commandv('playlist-next', 'force')
|
||||
else
|
||||
local next_file = get_adjacent_media_file(path, 'forward')
|
||||
if next_file then
|
||||
mp.commandv('loadfile', next_file)
|
||||
else
|
||||
mp.commandv('stop')
|
||||
end
|
||||
end
|
||||
|
||||
os.remove(ensure_absolute_path(path))
|
||||
end)
|
||||
mp.add_key_binding(nil, 'delete-file-quit', function()
|
||||
local path = mp.get_property_native('path')
|
||||
if is_protocol(path) then return end
|
||||
os.remove(ensure_absolute_path(path))
|
||||
mp.command('quit')
|
||||
end)
|
||||
|
Reference in New Issue
Block a user