first commit
This commit is contained in:
parent
77853d0c5a
commit
a2402cbf16
23
README.md
Normal file
23
README.md
Normal file
@ -0,0 +1,23 @@
|
||||
# recent.lua
|
||||
#### Recently played menu
|
||||
Logs played files to a history log file with an interactive 'recently played' menu that reads from the log. Allows for automatic or manual logging if you want a file bookmark menu instead.
|
||||
|
||||
|
||||
![](../assets/recent.png)
|
||||
### Menu controls:
|
||||
* Default display hotkey is **`` ` ``**
|
||||
* Keyboard:
|
||||
* `UP`/`DOWN` to move selection
|
||||
* `ENTER` to load highlighted entry
|
||||
* `DEL` to delete highlighted entry
|
||||
* `0`-`9` for quick selection
|
||||
* `ESC` to exit
|
||||
* Mouse (if turned on):
|
||||
* `WHEEL_UP`/`WHEEL_DOWN` to move selection
|
||||
* `MBTN_MID` to load highlighted entry
|
||||
* `MBTN_RIGHT` to exit
|
||||
#### Modify settings through `script-opts/recent.conf`
|
||||
* Log path is `history.log` inside whatever config directory mpv reads
|
||||
* Disabling `auto_save` makes it only save with a keybind
|
||||
* No save key is bound by default, see `script-opts`
|
||||
* See comments in the script for more info
|
318
recent.lua
Normal file
318
recent.lua
Normal file
@ -0,0 +1,318 @@
|
||||
local o = {
|
||||
-- Automatically save to log, otherwise only saves when requested
|
||||
-- you need to bind a save key if you disable it
|
||||
auto_save = true,
|
||||
save_bind = "",
|
||||
-- When automatically saving, skip entries with playback positions
|
||||
-- past this value, in percent. 100 saves all, around 95 is
|
||||
-- good for skipping videos that have reached final credits.
|
||||
auto_save_skip_past = 100,
|
||||
-- Runs automatically when --idle
|
||||
auto_run_idle = true,
|
||||
-- Write watch later for current file when switching
|
||||
write_watch_later = true,
|
||||
-- Display menu bind
|
||||
display_bind = "`",
|
||||
-- Middle click: Select; Right click: Exit;
|
||||
-- Scroll wheel: Up/Down
|
||||
mouse_controls = true,
|
||||
-- Reads from config directory or an absolute path
|
||||
log_path = "history.log",
|
||||
-- Date format in the log (see lua date formatting)
|
||||
date_format = "%d/%m/%y %X",
|
||||
-- Show file paths instead of media-title
|
||||
show_paths = false,
|
||||
-- Split paths to only show the file or show the full path
|
||||
split_paths = true,
|
||||
-- Font settings
|
||||
font_scale = 50,
|
||||
border_size = 0.7,
|
||||
-- Highlight color in BGR hexadecimal
|
||||
hi_color = "H46CFFF",
|
||||
-- Draw ellipsis at start/end denoting ommited entries
|
||||
ellipsis = false,
|
||||
-- Don't log certain media types
|
||||
ignore_audio = true,
|
||||
ignore_picture = true,
|
||||
ignore_video = false
|
||||
}
|
||||
(require "mp.options").read_options(o)
|
||||
local utils = require("mp.utils")
|
||||
o.log_path = utils.join_path(mp.find_config_file("."), o.log_path)
|
||||
|
||||
local cur_title, cur_path
|
||||
local list_drawn = false
|
||||
|
||||
function get_content_type()
|
||||
local track_list = mp.get_property_native("track-list")
|
||||
local file_type = ""
|
||||
for _, track in ipairs(track_list) do
|
||||
if track.type == "audio" then file_type = "audio"
|
||||
elseif not track.albumart and (track["demux-fps"] or 2) > 1 then
|
||||
file_type = "video"
|
||||
elseif track.type == "video" and not track.albumart and track["demux-fps"] == 1 then
|
||||
file_type = "picture"
|
||||
end
|
||||
end
|
||||
return file_type
|
||||
end
|
||||
|
||||
function esc_string(str)
|
||||
return str:gsub("([%p])", "%%%1")
|
||||
end
|
||||
|
||||
function get_path()
|
||||
local path = mp.get_property("path")
|
||||
local title = mp.get_property("media-title"):gsub("\"", "")
|
||||
if not path then return end
|
||||
if path:find("http.?://") or path:find("magnet:%?") or path:find("rtmp://") then
|
||||
return title, path
|
||||
else
|
||||
return title, utils.join_path(mp.get_property("working-directory"), path)
|
||||
end
|
||||
end
|
||||
|
||||
function unbind()
|
||||
if o.mouse_controls then
|
||||
mp.remove_key_binding("recent-WUP")
|
||||
mp.remove_key_binding("recent-WDOWN")
|
||||
mp.remove_key_binding("recent-MMID")
|
||||
mp.remove_key_binding("recent-MRIGHT")
|
||||
end
|
||||
mp.remove_key_binding("recent-UP")
|
||||
mp.remove_key_binding("recent-DOWN")
|
||||
mp.remove_key_binding("recent-ENTER")
|
||||
mp.remove_key_binding("recent-1")
|
||||
mp.remove_key_binding("recent-2")
|
||||
mp.remove_key_binding("recent-3")
|
||||
mp.remove_key_binding("recent-4")
|
||||
mp.remove_key_binding("recent-5")
|
||||
mp.remove_key_binding("recent-6")
|
||||
mp.remove_key_binding("recent-7")
|
||||
mp.remove_key_binding("recent-8")
|
||||
mp.remove_key_binding("recent-9")
|
||||
mp.remove_key_binding("recent-0")
|
||||
mp.remove_key_binding("recent-ESC")
|
||||
mp.remove_key_binding("recent-DEL")
|
||||
mp.set_osd_ass(0, 0, "")
|
||||
list_drawn = false
|
||||
end
|
||||
|
||||
function read_log(func)
|
||||
local f = io.open(o.log_path, "r")
|
||||
if not f then return end
|
||||
local list = {}
|
||||
for line in f:lines() do
|
||||
table.insert(list, (func(line)))
|
||||
end
|
||||
f:close()
|
||||
return list
|
||||
end
|
||||
|
||||
function read_log_table()
|
||||
return read_log(function(line)
|
||||
local t, p
|
||||
t, p = line:match("^.-\"(.-)\" | (.*)$")
|
||||
return {title = t, path = p}
|
||||
end)
|
||||
end
|
||||
|
||||
-- Write path to log on file end
|
||||
-- removing duplicates along the way
|
||||
function write_log(delete)
|
||||
if not cur_path then return end
|
||||
if o.ignore_audio and get_content_type() == "audio" then return
|
||||
elseif o.ignore_picture and get_content_type() == "picture" then return
|
||||
elseif o.ignore_video and get_content_type() == "video" then return end
|
||||
local content = read_log(function(line)
|
||||
if line:find(esc_string(cur_path)) then
|
||||
return nil
|
||||
else
|
||||
return line
|
||||
end
|
||||
end)
|
||||
f = io.open(o.log_path, "w+")
|
||||
if content then
|
||||
for i=1, #content do
|
||||
f:write(("%s\n"):format(content[i]))
|
||||
end
|
||||
end
|
||||
if not delete then
|
||||
f:write(("[%s] \"%s\" | %s\n"):format(os.date(o.date_format), cur_title, cur_path))
|
||||
end
|
||||
f:close()
|
||||
end
|
||||
|
||||
-- Display list on OSD and terminal
|
||||
function draw_list(list, start, choice)
|
||||
local msg = string.format("{\\fscx%f}{\\fscy%f}{\\bord%f}",
|
||||
o.font_scale, o.font_scale, o.border_size)
|
||||
local hi_start = string.format("{\\1c&H%s}", o.hi_color)
|
||||
local hi_end = "{\\1c&HFFFFFF}"
|
||||
if o.ellipsis then
|
||||
if start ~= 0 then
|
||||
msg = msg.."..."
|
||||
end
|
||||
msg = msg.."\\h\\N\\N"
|
||||
end
|
||||
local size = #list
|
||||
for i=1, math.min(10, size-start), 1 do
|
||||
local key = i % 10
|
||||
local p
|
||||
if o.show_paths then
|
||||
if o.split_paths and not list[size-start-i+1].path:find("^http.?://") then
|
||||
_, p = utils.split_path(list[size-start-i+1].path)
|
||||
else
|
||||
p = list[size-start-i+1].title or ""
|
||||
end
|
||||
else
|
||||
p = list[size-start-i+1].title or list[size-start-i+1].path or ""
|
||||
end
|
||||
if i == choice+1 then
|
||||
msg = msg..hi_start.."("..key..") "..p.."\\N\\N"..hi_end
|
||||
else
|
||||
msg = msg.."("..key..") "..p.."\\N\\N"
|
||||
end
|
||||
if not list_drawn then
|
||||
print("("..key..") "..p)
|
||||
end
|
||||
end
|
||||
if o.ellipsis and start+10 < size then
|
||||
msg = msg .."..."
|
||||
end
|
||||
mp.set_osd_ass(0, 0, msg)
|
||||
end
|
||||
|
||||
-- Handle up/down keys
|
||||
function select(list, start, choice, inc)
|
||||
choice = choice + inc
|
||||
if choice < 0 then
|
||||
choice = choice + 1
|
||||
start = start + inc
|
||||
elseif choice >= math.min(#list, 10) then
|
||||
choice = choice - 1
|
||||
start = start + inc
|
||||
end
|
||||
if start > math.max(#list-10, 0) then
|
||||
start = start - 1
|
||||
elseif start < 0 then
|
||||
start = start + 1
|
||||
end
|
||||
draw_list(list, start, choice)
|
||||
return start, choice
|
||||
end
|
||||
|
||||
-- Delete selected entry from the log
|
||||
function delete(list, start, choice)
|
||||
local playing_path = cur_path
|
||||
cur_path = list[#list-start-choice].path
|
||||
if not cur_path then
|
||||
print("Failed to delete")
|
||||
return
|
||||
end
|
||||
write_log(true)
|
||||
print("Deleted \""..cur_path.."\"")
|
||||
cur_path = playing_path
|
||||
end
|
||||
|
||||
-- Load file and remove binds
|
||||
function load(list, start, choice)
|
||||
unbind()
|
||||
if start+choice >= #list then return end
|
||||
if o.write_watch_later then
|
||||
mp.command("write-watch-later-config")
|
||||
end
|
||||
mp.commandv("loadfile", list[#list-start-choice].path, "replace")
|
||||
end
|
||||
|
||||
-- Display list and add keybinds
|
||||
function display_list()
|
||||
if list_drawn then
|
||||
unbind()
|
||||
return
|
||||
end
|
||||
local list = read_log_table()
|
||||
if not list or not list[1] then
|
||||
mp.osd_message("Log empty")
|
||||
return
|
||||
end
|
||||
local choice = 0
|
||||
local start = 0
|
||||
draw_list(list, start, choice)
|
||||
list_drawn = true
|
||||
|
||||
mp.add_forced_key_binding("UP", "recent-UP", function()
|
||||
start, choice = select(list, start, choice, -1)
|
||||
end, {repeatable=true})
|
||||
mp.add_forced_key_binding("DOWN", "recent-DOWN", function()
|
||||
start, choice = select(list, start, choice, 1)
|
||||
end, {repeatable=true})
|
||||
mp.add_forced_key_binding("ENTER", "recent-ENTER", function()
|
||||
load(list, start, choice)
|
||||
end)
|
||||
mp.add_forced_key_binding("DEL", "recent-DEL", function()
|
||||
delete(list, start, choice)
|
||||
list = read_log_table()
|
||||
if not list or not list[1] then
|
||||
unbind()
|
||||
return
|
||||
end
|
||||
start, choice = select(list, start, choice, 0)
|
||||
end)
|
||||
if o.mouse_controls then
|
||||
mp.add_forced_key_binding("WHEEL_UP", "recent-WUP", function()
|
||||
start, choice = select(list, start, choice, -1)
|
||||
end)
|
||||
mp.add_forced_key_binding("WHEEL_DOWN", "recent-WDOWN", function()
|
||||
start, choice = select(list, start, choice, 1)
|
||||
end)
|
||||
mp.add_forced_key_binding("MBTN_MID", "recent-MMID", function()
|
||||
load(list, start, choice)
|
||||
end)
|
||||
mp.add_forced_key_binding("MBTN_RIGHT", "recent-MRIGHT", function()
|
||||
unbind()
|
||||
end)
|
||||
end
|
||||
mp.add_forced_key_binding("1", "recent-1", function() load(list, start, 0) end)
|
||||
mp.add_forced_key_binding("2", "recent-2", function() load(list, start, 1) end)
|
||||
mp.add_forced_key_binding("3", "recent-3", function() load(list, start, 2) end)
|
||||
mp.add_forced_key_binding("4", "recent-4", function() load(list, start, 3) end)
|
||||
mp.add_forced_key_binding("5", "recent-5", function() load(list, start, 4) end)
|
||||
mp.add_forced_key_binding("6", "recent-6", function() load(list, start, 5) end)
|
||||
mp.add_forced_key_binding("7", "recent-7", function() load(list, start, 6) end)
|
||||
mp.add_forced_key_binding("8", "recent-8", function() load(list, start, 7) end)
|
||||
mp.add_forced_key_binding("9", "recent-9", function() load(list, start, 8) end)
|
||||
mp.add_forced_key_binding("0", "recent-0", function() load(list, start, 9) end)
|
||||
mp.add_forced_key_binding("ESC", "recent-ESC", function() unbind() end)
|
||||
end
|
||||
|
||||
if o.auto_save then
|
||||
-- Using hook, as at the "end-file" event the playback position info is already unset.
|
||||
mp.add_hook("on_unload", 9, function ()
|
||||
local pos = mp.get_property("percent-pos")
|
||||
if not pos then return end
|
||||
if tonumber(pos) <= o.auto_save_skip_past then
|
||||
write_log(false)
|
||||
else
|
||||
write_log(true)
|
||||
end
|
||||
end)
|
||||
else
|
||||
mp.add_key_binding(o.save_bind, "recent-save", function()
|
||||
write_log(false)
|
||||
mp.osd_message("Saved entry to log")
|
||||
end)
|
||||
end
|
||||
|
||||
if o.auto_run_idle then
|
||||
mp.observe_property("idle-active", "bool", function(_, v)
|
||||
if v then display_list() end
|
||||
end)
|
||||
end
|
||||
|
||||
mp.register_event("file-loaded", function()
|
||||
unbind()
|
||||
cur_title, cur_path = get_path()
|
||||
end)
|
||||
|
||||
mp.add_key_binding(o.display_bind, "display-recent", display_list)
|
18
script-opts/recent.conf
Normal file
18
script-opts/recent.conf
Normal file
@ -0,0 +1,18 @@
|
||||
auto_save=yes
|
||||
save_bind=
|
||||
auto_save_skip_past=
|
||||
auto_run_idle=yes
|
||||
write_watch_later=yes
|
||||
display_bind=`
|
||||
mouse_controls=yes
|
||||
log_path=history.log
|
||||
date_format=%d/%m/%y %X
|
||||
show_paths=no
|
||||
split_paths=yes
|
||||
font_scale=50
|
||||
border_size=0.7
|
||||
hi_color=H46CFFF
|
||||
ellipsis=no
|
||||
ignore_audio=no
|
||||
ignore_picture=no
|
||||
ignore_video=no
|
Loading…
Reference in New Issue
Block a user