modern_lisp-machine/.config/mpv/scripts/SimpleHistory.lua

2276 lines
96 KiB
Lua
Raw Normal View History

2024-06-10 01:02:20 +00:00
-- Copyright (c) 2022, Eisa AlAwadhi
-- License: BSD 2-Clause License
-- Creator: Eisa AlAwadhi
-- Project: SimpleHistory
-- Version: 1.1.6
local o = {
---------------------------USER CUSTOMIZATION SETTINGS---------------------------
--These settings are for users to manually change some options.
--Changes are recommended to be made in the script-opts directory.
-----Script Settings----
auto_run_list_idle = 'recents', --Auto run the list when opening mpv and there is no video / file loaded. 'none' for disabled. Or choose between: 'all', 'recents', 'distinct', 'protocols', 'fileonly', 'titleonly', 'timeonly', 'keywords'.
startup_idle_behavior = 'none', --The behavior when mpv launches and nothing is loaded. 'none' for disabled. 'resume' to automatically resume your last played item. 'resume-notime' to resume your last played item but starts from the beginning.
toggle_idlescreen = false, --hides OSC idle screen message when opening and closing menu (could cause unexpected behavior if multiple scripts are triggering osc-idlescreen off)
resume_offset = -0.65, --change to 0 so item resumes from the exact position, or decrease the value so that it gives you a little preview before loading the resume point
osd_messages = true, --true is for displaying osd messages when actions occur. Change to false will disable all osd messages generated from this script
resume_option = 'notification', --'none': for disabled. 'notification': a message to resume the previous reached time will be triggered. 'force': to forcefully resume last playback based on threshold
resume_option_threshold = 2, --0 to always trigger the resume option when the same video has been played previously, a value such as 5 will only trigger the resume option if the last played time starts after 5% of the video and ends before completion by 5%
mark_history_as_chapter = false, --true is for marking the time as a chapter. false disables mark as chapter behavior.
invert_history_blacklist = false, --true so that blacklist becomes a whitelist, resulting in stuff such as paths / websites that are added to history_blacklist to be saved into history
history_blacklist=[[
[""]
]], --Paths / URLs / Websites / Files / Protocols / Extensions, that wont be added to history automatically, e.g.: ["c:\\users\\eisa01\\desktop", "c:\\users\\eisa01\\desktop\\*", "c:\\temp\\naruto-01.mp4", "youtube.com", "https://dailymotion.com/", "avi", "https://www.youtube.com/watch?v=e8YBesRKq_U", ".jpeg", "magnet:", "https://", "ftp"]
history_resume_keybind=[[
["ctrl+r", "ctrl+R"]
]], --Keybind that will be used to immediately load and resume last item when no video is playing. If video is playing it will resume to the last found position
history_load_last_keybind=[[
["alt+r", "alt+R"]
]], --Keybind that will be used to immediately load the last item without resuming when no video is playing. If video is playing then it will add into playlist
open_list_keybind=[[
[ ["h", "all"], ["H", "all"], ["r", "recents"], ["R", "recents"] ]
]], --Keybind that will be used to open the list along with the specified filter. Available filters: 'all', 'recents', 'distinct', 'protocols', 'fileonly', 'titleonly', 'timeonly', 'keywords'.
list_filter_jump_keybind=[[
[ ["h", "all"], ["H", "all"], ["r", "recents"], ["R", "recents"], ["d", "distinct"], ["D", "distinct"], ["f", "fileonly"], ["F", "fileonly"] ]
]], --Keybind that is used while the list is open to jump to the specific filter (it also enables pressing a filter keybind twice to close list). Available fitlers: 'all', 'recents', 'distinct', 'protocols', 'fileonly', 'titleonly', 'timeonly', 'keywords'.
-----Incognito Settings----
auto_run_incognito_mode = false, --true to automatically start incognito mode when mpv launches, false disables this behavior
delete_incognito_entry = true, --true so that the file that had incognito mode triggered on gets removed from history automatically, false keeps the file in history that incognito mode triggered on
restore_incognito_entry = 'always', --'none' for disabled, 'deleted-restore' so that the the file that was removed when entering incognito automtically gets restored, 'always' so that exiting incognito_mode always immediately updates entry into history
history_incognito_mode_keybind=[[
["ctrl+H"]
]], --Triggers incognito mode. When enabled files played wont be added to history until this mode is disabled.
-----Logging Settings-----
log_path = '/:dir%mpvconf%', --Change to '/:dir%script%' for placing it in the same directory of script, OR change to '/:dir%mpvconf%' for mpv portable_config directory. OR write any variable using '/:var' then the variable '/:var%APPDATA%' you can use path also, such as: '/:var%APPDATA%\\mpv' OR '/:var%HOME%/mpv' OR specify the absolute path , e.g.: 'C:\\Users\\Eisa01\\Desktop\\'
log_file = 'mpvHistory.log', --name+extension of the file that will be used to store the log data
date_format = '%A/%B %d/%m/%Y %X', --Date format in the log (see lua date formatting), e.g.:'%d/%m/%y %X' or '%d/%b/%y %X'
file_title_logging = 'protocols', --Change between 'all', 'protocols', 'none'. This option will store the media title in log file, it is useful for websites / protocols because title cannot be parsed from links alone
logging_protocols=[[
["https?://", "magnet:", "rtmp:"]
]], --add above (after a comma) any protocol you want its title to be stored in the log file. This is valid only for (file_title_logging = 'protocols' or file_title_logging = 'all')
prefer_filename_over_title = 'local', --Prefers to log filename over filetitle. Select between 'local', 'protocols', 'all', and 'none'. 'local' prefer filenames for videos that are not protocols. 'protocols' will prefer filenames for protocols only. 'all' will prefer filename over filetitle for both protocols and not protocols videos. 'none' will always use filetitle instead of filename
same_entry_limit = 2, --Limit saving entries with same path: -1 for unlimited, 0 will always update entries of same path, e.g. value of 3 will have the limit of 3 then it will start updating old values on the 4th entry.
-----List Settings-----
loop_through_list = false, --true is for going up on the first item loops towards the last item and vise-versa. false disables this behavior.
list_middle_loader = true, --false is for more items to show, then u must reach the end. true is for new items to show after reaching the middle of list.
show_paths = false, --Show file paths instead of media-title
show_item_number = true, --Show the number of each item before displaying its name and values.
slice_longfilenames = false, --Change to true or false. Slices long filenames per the amount specified below
slice_longfilenames_amount = 55, --Amount for slicing long filenames
list_show_amount = 10, --Change maximum number to show items at once
quickselect_0to9_keybind = true, --Keybind entries from 0 to 9 for quick selection when list is open (list_show_amount = 10 is maximum for this feature to work)
main_list_keybind_twice_exits = true, --Will exit the list when double tapping the main list, even if the list was accessed through a different filter.
search_not_typing_smartly = true, --To smartly set the search as not typing (when search box is open) without needing to press ctrl+enter.
search_behavior = 'any', --'specific' to find a match of either a date, title, path / url, time. 'any' to find any typed search based on combination of date, title, path / url, and time. 'any-notime' to find any typed search based on combination of date, title, and path / url, but without looking for time (this is to reduce unwanted results).
-----Filter Settings------
--available filters: "all" to display all the items. Or "recents" to display recently added items to log without duplicate. Or "distinct" to show recent saved entries for files in different paths. Or "fileonly" to display files saved without time. Or "timeonly" to display files that have time only. Or "keywords" to display files with matching keywords specified in the configuration. Or "playing" to show list of current playing file.
filters_and_sequence=[[
["all", "recents", "distinct", "protocols", "playing", "fileonly", "titleonly", "keywords"]
]], --Jump to the following filters and in the shown sequence when navigating via left and right keys. You can change the sequence and delete filters that are not needed.
next_filter_sequence_keybind=[[
["RIGHT", "MBTN_FORWARD"]
]], --Keybind that will be used to go to the next available filter based on the filters_and_sequence
previous_filter_sequence_keybind=[[
["LEFT", "MBTN_BACK"]
]], --Keybind that will be used to go to the previous available filter based on the filters_and_sequence
loop_through_filters = true, --true is for bypassing the last filter to go to first filter when navigating through filters using arrow keys, and vice-versa. false disables this behavior.
keywords_filter_list=[[
[""]
]], --Create a filter out of your desired 'keywords', e.g.: youtube.com will filter out the videos from youtube. You can also insert a portion of filename or title, or extension or a full path / portion of a path. e.g.: ["youtube.com", "mp4", "naruto", "c:\\users\\eisa01\\desktop"]
-----Sort Settings------
--available sort: 'added-asc' is for the newest added item to show first. Or 'added-desc' for the newest added to show last. Or 'alphanum-asc' is for A to Z approach with filename and episode number lower first. Or 'alphanum-desc' is for its Z to A approach. Or 'time-asc', 'time-desc' to sort the list based on time.
list_default_sort = 'added-asc', --the default sorting method for all the different filters in the list. select between 'added-asc', 'added-desc', 'time-asc', 'time-desc', 'alphanum-asc', 'alphanum-desc'
list_filters_sort=[[
[ ]
]], --Default sort for specific filters, e.g.: [ ["all", "alphanum-asc"], ["playing", "added-desc"] ]
list_cycle_sort_keybind=[[
["alt+s", "alt+S"]
]], --Keybind to cycle through the different available sorts when list is open
-----List Design Settings-----
list_alignment = 7, --The alignment for the list, uses numpad positions choose from 1-9 or 0 to disable. e,g.:7 top left alignment, 8 top middle alignment, 9 top right alignment.
text_time_type = 'duration', --The time type for items on the list. Select between 'duration', 'length', 'remaining'.
time_seperator = ' 🕒 ', --Time seperator that will be used before the time
list_sliced_prefix = '...\\h\\N\\N', --The text that indicates there are more items above. \\N is for new line. \\h is for hard space.
list_sliced_suffix = '...', --The text that indicates there are more items below.
quickselect_0to9_pre_text = false, --true enables pre text for showing quickselect keybinds before the list. false to disable
text_color = 'ffffff', --Text color for list in BGR hexadecimal
text_scale = 50, --Font size for the text of list
text_border = 0.7, --Black border size for the text of list
text_cursor_color = 'ffbf7f', --Text color of current cursor position in BGR hexadecimal
text_cursor_scale = 50, --Font size for text of current cursor position in list
text_cursor_border = 0.7, --Black border size for text of current cursor position in list
text_highlight_pre_text = '', --Pre text for highlighted multi-select item
search_color_typing = 'ffffaa', --Search color when in typing mode
search_color_not_typing = '00bfff', --Search color when not in typing mode and it is active
header_color = '00bfff', --Header color in BGR hexadecimal
header_scale = 55, --Header text size for the list
header_border = 0.8, --Black border size for the Header of list
header_text = '⌛ History [%cursor%/%total%]%prehighlight%%highlight%%afterhighlight%%prelistduration%%listduration%%afterlistduration%%prefilter%%filter%%afterfilter%%presort%%sort%%aftersort%%presearch%%search%%aftersearch%', --Text to be shown as header for the list
--Available header variables: %cursor%, %total%, %highlight%, %filter%, %search%, %listduration%, %listlength%, %listremaining%
--User defined text that only displays if a variable is triggered: %prefilter%, %afterfilter%, %prehighlight%, %afterhighlight% %presearch%, %aftersearch%, %prelistduration%, %afterlistduration%, %prelistlength%, %afterlistlength%, %prelistremaining%, %afterlistremaining%
--Variables explanation: %cursor: displays the number of cursor position in list. %total: total amount of items in current list. %highlight%: total number of highlighted items. %filter: shows the filter name, %search: shows the typed search. Example of user defined text that only displays if a variable is triggered of user: %prefilter: user defined text before showing filter, %afterfilter: user defined text after showing filter.
header_sort_hide_text = 'added-asc',--Sort method that is hidden from header when using %sort% variable
header_sort_pre_text = ' \\{',--Text to be shown before sort in the header, when using %presort%
header_sort_after_text = '}',--Text to be shown after sort in the header, when using %aftersort%
header_filter_pre_text = ' [Filter: ', --Text to be shown before filter in the header, when using %prefilter%
header_filter_after_text = ']', --Text to be shown after filter in the header, when using %afterfilter%
header_search_pre_text = '\\h\\N\\N[Search=', --Text to be shown before search in the header, when using %presearch%
header_search_after_text = '..]', --Text to be shown after search in the header, when using %aftersearch%
header_highlight_pre_text = '', --Text to be shown before total highlighted items of displayed list in the header
header_highlight_after_text = '', --Text to be shown after total highlighted items of displayed list in the header
header_list_duration_pre_text = ' 🕒 ', --Text to be shown before playback total duration of displayed list in the header
header_list_duration_after_text = '', --Text to be shown after playback total duration of displayed list in the header
header_list_length_pre_text = ' 🕒 ', --Text to be shown before playback total duration of displayed list in the header
header_list_length_after_text = '', --Text to be shown after playback total duration of displayed list in the header
header_list_remaining_pre_text = ' 🕒 ', --Text to be shown before playback total duration of displayed list in the header
header_list_remaining_after_text = '', --Text to be shown after playback total duration of displayed list in the header
-----Time Format Settings-----
--in the first parameter, you can define from the available styles: default, hms, hms-full, timestamp, timestamp-concise "default" to show in HH:MM:SS.sss format. "hms" to show in 1h 2m 3.4s format. "hms-full" is the same as hms but keeps the hours and minutes persistent when they are 0. "timestamp" to show the total time as timestamp 123456.700 format. "timestamp-concise" shows the total time in 123456.7 format (shows and hides decimals depending on availability).
--in the second parameter, you can define whether to show milliseconds, round them or truncate them. Available options: 'truncate' to remove the milliseconds and keep the seconds. 0 to remove the milliseconds and round the seconds. 1 or above is the amount of milliseconds to display. The default value is 3 milliseconds.
--in the third parameter you can define the seperator between hour:minute:second. "default" style is automatically set to ":", "hms", "hms-full" are automatically set to " ". You can define your own. Some examples: ["default", 3, "-"],["hms-full", 5, "."],["hms", "truncate", ":"],["timestamp-concise"],["timestamp", 0],["timestamp", "truncate"],["timestamp", 5]
osd_time_format=[[
["default", "truncate"]
]],
list_time_format=[[
["default", "truncate"]
]],
header_duration_time_format=[[
["hms", "truncate", ":"]
]],
header_length_time_format=[[
["hms", "truncate", ":"]
]],
header_remaining_time_format=[[
["hms", "truncate", ":"]
]],
-----List Keybind Settings-----
--Add below (after a comma) any additional keybind you want to bind. Or change the letter inside the quotes to change the keybind
--Example of changing and adding keybinds: --From ["b", "B"] To ["b"]. --From [""] to ["alt+b"]. --From [""] to ["a" "ctrl+a", "alt+a"]
list_move_up_keybind=[[
["UP", "WHEEL_UP"]
]], --Keybind that will be used to navigate up on the list
list_move_down_keybind=[[
["DOWN", "WHEEL_DOWN"]
]], --Keybind that will be used to navigate down on the list
list_page_up_keybind=[[
["PGUP"]
]], --Keybind that will be used to go to the first item for the page shown on the list
list_page_down_keybind=[[
["PGDWN"]
]], --Keybind that will be used to go to the last item for the page shown on the list
list_move_first_keybind=[[
["HOME"]
]], --Keybind that will be used to navigate to the first item on the list
list_move_last_keybind=[[
["END"]
]], --Keybind that will be used to navigate to the last item on the list
list_highlight_move_keybind=[[
["SHIFT"]
]], --Keybind that will be used to highlight while pressing a navigational keybind, keep holding shift and then press any navigation keybind, such as: up, down, home, pgdwn, etc..
list_highlight_all_keybind=[[
["ctrl+a", "ctrl+A"]
]], --Keybind that will be used to highlight all displayed items on the list
list_unhighlight_all_keybind=[[
["ctrl+d", "ctrl+D"]
]], --Keybind that will be used to remove all currently highlighted items from the list
list_select_keybind=[[
["ENTER", "MBTN_MID"]
]], --Keybind that will be used to load entry based on cursor position
list_add_playlist_keybind=[[
["CTRL+ENTER"]
]], --Keybind that will be used to add entry to playlist based on cursor position
list_add_playlist_highlighted_keybind=[[
["SHIFT+ENTER"]
]], --Keybind that will be used to add all highlighted entries to playlist
list_close_keybind=[[
["ESC", "MBTN_RIGHT"]
]], --Keybind that will be used to close the list (closes search first if it is open)
list_delete_keybind=[[
["DEL"]
]], --Keybind that will be used to delete the entry based on cursor position
list_delete_highlighted_keybind=[[
["SHIFT+DEL"]
]], --Keybind that will be used to delete all highlighted entries from the list
list_search_activate_keybind=[[
["ctrl+f", "ctrl+F"]
]], --Keybind that will be used to trigger search
list_search_not_typing_mode_keybind=[[
["ALT+ENTER"]
]], --Keybind that will be used to exit typing mode of search while keeping search open
list_ignored_keybind=[[
["B", "b", "k", "K", "c", "C"]
]], --Keybind thats are ignored when list is open
---------------------------END OF USER CUSTOMIZATION SETTINGS---------------------------
}
(require 'mp.options').read_options(o)
local utils = require 'mp.utils'
local msg = require 'mp.msg'
o.history_blacklist = utils.parse_json(o.history_blacklist)
o.history_incognito_mode_keybind = utils.parse_json(o.history_incognito_mode_keybind)
o.filters_and_sequence = utils.parse_json(o.filters_and_sequence)
o.keywords_filter_list = utils.parse_json(o.keywords_filter_list)
o.list_filters_sort = utils.parse_json(o.list_filters_sort)
o.logging_protocols = utils.parse_json(o.logging_protocols)
o.history_resume_keybind = utils.parse_json(o.history_resume_keybind)
o.history_load_last_keybind = utils.parse_json(o.history_load_last_keybind)
o.osd_time_format = utils.parse_json(o.osd_time_format)
o.list_time_format = utils.parse_json(o.list_time_format)
o.header_duration_time_format = utils.parse_json(o.header_duration_time_format)
o.header_length_time_format = utils.parse_json(o.header_length_time_format)
o.header_remaining_time_format = utils.parse_json(o.header_remaining_time_format)
o.list_move_up_keybind = utils.parse_json(o.list_move_up_keybind)
o.list_move_down_keybind = utils.parse_json(o.list_move_down_keybind)
o.list_page_up_keybind = utils.parse_json(o.list_page_up_keybind)
o.list_page_down_keybind = utils.parse_json(o.list_page_down_keybind)
o.list_move_first_keybind = utils.parse_json(o.list_move_first_keybind)
o.list_move_last_keybind = utils.parse_json(o.list_move_last_keybind)
o.list_highlight_move_keybind = utils.parse_json(o.list_highlight_move_keybind)
o.list_highlight_all_keybind = utils.parse_json(o.list_highlight_all_keybind)
o.list_unhighlight_all_keybind = utils.parse_json(o.list_unhighlight_all_keybind)
o.list_cycle_sort_keybind = utils.parse_json(o.list_cycle_sort_keybind)
o.list_select_keybind = utils.parse_json(o.list_select_keybind)
o.list_add_playlist_keybind = utils.parse_json(o.list_add_playlist_keybind)
o.list_add_playlist_highlighted_keybind = utils.parse_json(o.list_add_playlist_highlighted_keybind)
o.list_close_keybind = utils.parse_json(o.list_close_keybind)
o.list_delete_keybind = utils.parse_json(o.list_delete_keybind)
o.list_delete_highlighted_keybind = utils.parse_json(o.list_delete_highlighted_keybind)
o.list_search_activate_keybind = utils.parse_json(o.list_search_activate_keybind)
o.list_search_not_typing_mode_keybind = utils.parse_json(o.list_search_not_typing_mode_keybind)
o.next_filter_sequence_keybind = utils.parse_json(o.next_filter_sequence_keybind)
o.previous_filter_sequence_keybind = utils.parse_json(o.previous_filter_sequence_keybind)
o.open_list_keybind = utils.parse_json(o.open_list_keybind)
o.list_filter_jump_keybind = utils.parse_json(o.list_filter_jump_keybind)
o.list_ignored_keybind = utils.parse_json(o.list_ignored_keybind)
if utils.shared_script_property_set then
utils.shared_script_property_set('simplehistory-menu-open', 'no')
end
mp.set_property('user-data/simplehistory/menu-open', 'no')
if string.lower(o.log_path) == '/:dir%mpvconf%' then
o.log_path = mp.find_config_file('.')
elseif string.lower(o.log_path) == '/:dir%script%' then
o.log_path = debug.getinfo(1).source:match('@?(.*/)')
elseif o.log_path:match('/:var%%(.*)%%') then
local os_variable = o.log_path:match('/:var%%(.*)%%')
o.log_path = o.log_path:gsub('/:var%%(.*)%%', os.getenv(os_variable))
end
local log_fullpath = utils.join_path(o.log_path, o.log_file)
local log_length_text = 'length='
local log_time_text = 'time='
local protocols = {'https?:', 'magnet:', 'rtmps?:', 'smb:', 'ftps?:', 'sftp:'}
local available_filters = {'all', 'recents', 'distinct', 'playing', 'protocols', 'fileonly', 'titleonly', 'timeonly', 'keywords'}
local available_sorts = {'added-asc', 'added-desc', 'time-asc', 'time-desc', 'alphanum-asc', 'alphanum-desc'}
local search_string = ''
local search_active = false
local incognito_mode = false
local autosaved_entry = false
local incognito_auto_run_triggered = false
local loadTriggered = false --1.1.5# to identify if load is triggered atleast once for idle option
local resume_selected = false
local list_contents = {}
local list_start = 0
local list_cursor = 1
local list_highlight_cursor = {}
local list_drawn = false
local list_pages = {}
local filePath, fileTitle, fileLength
local seekTime = 0
local logTime = 0 --1.3# use logTime since seekTime is used in multiple places
local filterName = 'all'
local sortName
function starts_protocol(tab, val)
for index, value in ipairs(tab) do
if (val:find(value) == 1) then
return true
end
end
return false
end
function contain_value(tab, val)
if not tab then return msg.error('check value passed') end
if not val then return msg.error('check value passed') end
for index, value in ipairs(tab) do
if value.match(string.lower(val), string.lower(value)) then
return true
end
end
return false
end
function has_value(tab, val, array2d)
if not tab then return msg.error('check value passed') end
if not val then return msg.error('check value passed') end
if not array2d then
for index, value in ipairs(tab) do
if string.lower(value) == string.lower(val) then
return true
end
end
end
if array2d then
for i=1, #tab do
if tab[i] and string.lower(tab[i][array2d]) == string.lower(val) then
return true
end
end
end
return false
end
function file_exists(name)
local f = io.open(name, "r")
if f ~= nil then io.close(f) return true else return false end
end
function format_time(seconds, sep, decimals, style)
local function divmod (a, b)
return math.floor(a / b), a % b
end
decimals = decimals == nil and 3 or decimals
local s = seconds
local h, s = divmod(s, 60*60)
local m, s = divmod(s, 60)
if decimals == 'truncate' then
s = math.floor(s)
decimals = 0
if style == 'timestamp' then
seconds = math.floor(seconds)
end
end
if not style or style == '' or style == 'default' then
local second_format = string.format("%%0%d.%df", 2+(decimals > 0 and decimals+1 or 0), decimals)
sep = sep and sep or ":"
return string.format("%02d"..sep.."%02d"..sep..second_format, h, m, s)
elseif style == 'hms' or style == 'hms-full' then
sep = sep ~= nil and sep or " "
if style == 'hms-full' or h > 0 then
return string.format("%dh"..sep.."%dm"..sep.."%." .. tostring(decimals) .. "fs", h, m, s)
elseif m > 0 then
return string.format("%dm"..sep.."%." .. tostring(decimals) .. "fs", m, s)
else
return string.format("%." .. tostring(decimals) .. "fs", s)
end
elseif style == 'timestamp' then
return string.format("%." .. tostring(decimals) .. "f", seconds)
elseif style == 'timestamp-concise' then
return seconds
end
end
function get_file()
local path = mp.get_property('path')
if not path then return end
local length = (mp.get_property_number('duration') or 0)
local title = mp.get_property('media-title'):gsub("\"", "")
if starts_protocol(o.logging_protocols, path) and o.prefer_filename_over_title == 'protocols' then
title = mp.get_property('filename'):gsub("\"", "")
elseif not starts_protocol(o.logging_protocols, path) and o.prefer_filename_over_title == 'local' then
title = mp.get_property('filename'):gsub("\"", "")
elseif o.prefer_filename_over_title == 'all' then
title = mp.get_property('filename'):gsub("\"", "")
end
return path, title, length
end
function bind_keys(keys, name, func, opts)
if not keys then
mp.add_forced_key_binding(keys, name, func, opts)
return
end
for i = 1, #keys do
if i == 1 then
mp.add_forced_key_binding(keys[i], name, func, opts)
else
mp.add_forced_key_binding(keys[i], name .. i, func, opts)
end
end
end
function unbind_keys(keys, name)
if not keys then
mp.remove_key_binding(name)
return
end
for i = 1, #keys do
if i == 1 then
mp.remove_key_binding(name)
else
mp.remove_key_binding(name .. i)
end
end
end
function esc_string(str)
return str:gsub("([%p])", "%%%1")
end
---------Start of LogManager---------
--LogManager (Read and Format the List from Log)--
function read_log(func)
local f = io.open(log_fullpath, "r")
if not f then return end
local contents = {}
for line in f:lines() do
table.insert(contents, (func(line)))
end
f:close()
return contents
end
function read_log_table()
local line_pos = 0
return read_log(function(line)
local tt, p, t, s, d, n, e, l, dt, ln, r
if line:match('^.-\"(.-)\"') then
tt = line:match('^.-\"(.-)\"')
n, p = line:match('^.-\"(.-)\" | (.*) | ' .. esc_string(log_length_text) .. '(.*)')
else
p = line:match('[(.*)%]]%s(.*) | ' .. esc_string(log_length_text) .. '(.*)')
d, n, e = p:match('^(.-)([^\\/]-)%.([^\\/%.]-)%.?$')
end
dt = line:match('%[(.-)%]')
t = line:match(' | ' .. esc_string(log_time_text) .. '(%d*%.?%d*)(.*)$')
ln = line:match(' | ' .. esc_string(log_length_text) .. '(%d*%.?%d*)(.*)$')
r = tonumber(ln) - tonumber(t)
l = line
line_pos = line_pos + 1
return {found_path = p, found_time = t, found_name = n, found_title = tt, found_line = l, found_sequence = line_pos, found_directory = d, found_datetime = dt, found_length = ln, found_remaining = r}
end)
end
function list_sort(tab, sort)
if sort == 'added-asc' then
table.sort(tab, function(a, b) return a['found_sequence'] < b['found_sequence'] end)
elseif sort == 'added-desc' then
table.sort(tab, function(a, b) return a['found_sequence'] > b['found_sequence'] end)
elseif sort == 'time-asc' then
table.sort(tab, function(a, b) return tonumber(a['found_time']) > tonumber(b['found_time']) end)
elseif sort == 'time-desc' then
table.sort(tab, function(a, b) return tonumber(a['found_time']) < tonumber(b['found_time']) end)
elseif sort == 'alphanum-asc' or sort == 'alphanum-desc' then
local function padnum(d) local dec, n = string.match(d, "(%.?)0*(.+)")
return #dec > 0 and ("%.12f"):format(d) or ("%s%03d%s"):format(dec, #n, n) end
if sort == 'alphanum-asc' then
table.sort(tab, function(a, b) return tostring(a['found_path']):gsub("%.?%d+", padnum) .. ("%3d"):format(#b) > tostring(b['found_path']):gsub("%.?%d+", padnum) .. ("%3d"):format(#a) end)
elseif sort == 'alphanum-desc' then
table.sort(tab, function(a, b) return tostring(a['found_path']):gsub("%.?%d+", padnum) .. ("%3d"):format(#b) < tostring(b['found_path']):gsub("%.?%d+", padnum) .. ("%3d"):format(#a) end)
end
end
return tab
end
function parse_header(string)
local osd_header_color = string.format("{\\1c&H%s}", o.header_color)
local osd_search_color = osd_header_color
if search_active == 'typing' then
osd_search_color = string.format("{\\1c&H%s}", o.search_color_typing)
elseif search_active == 'not_typing' then
osd_search_color = string.format("{\\1c&H%s}", o.search_color_not_typing)
end
local osd_msg_end = "{\\1c&HFFFFFF}"
string = string:gsub("%%total%%", #list_contents)
:gsub("%%cursor%%", list_cursor)
if filterName ~= 'all' then
string = string:gsub("%%filter%%", filterName)
:gsub("%%prefilter%%", o.header_filter_pre_text)
:gsub("%%afterfilter%%", o.header_filter_after_text)
else
string = string:gsub("%%filter%%", '')
:gsub("%%prefilter%%", '')
:gsub("%%afterfilter%%", '')
end
local list_total_duration = 0
if string:match('%listduration%%') then
list_total_duration = get_total_duration('found_time')
if list_total_duration > 0 then
string = string:gsub("%%listduration%%", format_time(list_total_duration, o.header_duration_time_format[3], o.header_duration_time_format[2], o.header_duration_time_format[1]))
else
string = string:gsub("%%listduration%%", '')
end
end
if list_total_duration > 0 then
string = string:gsub("%%prelistduration%%", o.header_list_duration_pre_text)
:gsub("%%afterlistduration%%", o.header_list_duration_after_text)
else
string = string:gsub("%%prelistduration%%", '')
:gsub("%%afterlistduration%%", '')
end
local list_total_length = 0
if string:match('%listlength%%') then
list_total_length = get_total_duration('found_length')
if list_total_length > 0 then
string = string:gsub("%%listlength%%", format_time(list_total_length, o.header_length_time_format[3], o.header_length_time_format[2], o.header_length_time_format[1]))
else
string = string:gsub("%%listlength%%", '')
end
end
if list_total_length > 0 then
string = string:gsub("%%prelistlength%%", o.header_list_length_pre_text)
:gsub("%%afterlistlength%%", o.header_list_length_after_text)
else
string = string:gsub("%%prelistlength%%", '')
:gsub("%%afterlistlength%%", '')
end
local list_total_remaining = 0
if string:match('%listremaining%%') then
list_total_remaining = get_total_duration('found_remaining')
if list_total_remaining > 0 then
string = string:gsub("%%listremaining%%", format_time(list_total_remaining, o.header_remaining_time_format[3], o.header_remaining_time_format[2], o.header_remaining_time_format[1]))
else
string = string:gsub("%%listremaining%%", '')
end
end
if list_total_remaining > 0 then
string = string:gsub("%%prelistremaining%%", o.header_list_remaining_pre_text)
:gsub("%%afterlistremaining%%", o.header_list_remaining_after_text)
else
string = string:gsub("%%prelistremaining%%", '')
:gsub("%%afterlistremaining%%", '')
end
if #list_highlight_cursor > 0 then
string = string:gsub("%%highlight%%", #list_highlight_cursor)
:gsub("%%prehighlight%%", o.header_highlight_pre_text)
:gsub("%%afterhighlight%%", o.header_highlight_after_text)
else
string = string:gsub("%%highlight%%", '')
:gsub("%%prehighlight%%", '')
:gsub("%%afterhighlight%%", '')
end
if sortName and sortName ~= o.header_sort_hide_text then
string = string:gsub("%%sort%%", sortName)
:gsub("%%presort%%", o.header_sort_pre_text)
:gsub("%%aftersort%%", o.header_sort_after_text)
else
string = string:gsub("%%sort%%", '')
:gsub("%%presort%%", '')
:gsub("%%aftersort%%", '')
end
if search_active then
local search_string_osd = search_string
if search_string_osd ~= '' then
search_string_osd = search_string:gsub('%%', '%%%%%%%%'):gsub('\\', '\\'):gsub('{', '\\{')
end
string = string:gsub("%%search%%", osd_search_color..search_string_osd..osd_header_color)
:gsub("%%presearch%%", o.header_search_pre_text)
:gsub("%%aftersearch%%", o.header_search_after_text)
else
string = string:gsub("%%search%%", '')
:gsub("%%presearch%%", '')
:gsub("%%aftersearch%%", '')
end
string = string:gsub("%%%%", "%%")
return string
end
function get_list_contents(filter, sort)
if not filter then filter = filterName end
if not sort then sort = get_list_sort(filter) end
local current_sort
local filtered_table = {}
local prev_list_contents
if list_contents ~= nil and list_contents[1] then
prev_list_contents = list_contents
else
prev_list_contents = read_log_table()
end
list_contents = read_log_table()
if not list_contents and not search_active or not list_contents[1] and not search_active then return end
current_sort = 'added-asc'
if filter == 'recents' then
table.sort(list_contents, function(a, b) return a['found_sequence'] < b['found_sequence'] end)
local unique_values = {}
local list_total = #list_contents
if filePath == list_contents[#list_contents].found_path and tonumber(list_contents[#list_contents].found_time) == 0 then
list_total = list_total -1
end
for i = list_total, 1, -1 do
if not has_value(unique_values, list_contents[i].found_path) then
table.insert(unique_values, list_contents[i].found_path)
table.insert(filtered_table, list_contents[i])
end
end
table.sort(filtered_table, function(a, b) return a['found_sequence'] < b['found_sequence'] end)
list_contents = filtered_table
end
if filter == 'distinct' then
table.sort(list_contents, function(a, b) return a['found_sequence'] < b['found_sequence'] end)
local unique_values = {}
local list_total = #list_contents
if filePath == list_contents[#list_contents].found_path and tonumber(list_contents[#list_contents].found_time) == 0 then
list_total = list_total -1
end
for i = list_total, 1, -1 do
if list_contents[i].found_directory and not has_value(unique_values, list_contents[i].found_directory) and not starts_protocol(protocols, list_contents[i].found_path) then
table.insert(unique_values, list_contents[i].found_directory)
table.insert(filtered_table, list_contents[i])
end
end
table.sort(filtered_table, function(a, b) return a['found_sequence'] < b['found_sequence'] end)
list_contents = filtered_table
end
if filter == 'fileonly' then
for i = 1, #list_contents do
if tonumber(list_contents[i].found_time) == 0 then
table.insert(filtered_table, list_contents[i])
end
end
list_contents = filtered_table
end
if filter == 'timeonly' then
for i = 1, #list_contents do
if tonumber(list_contents[i].found_time) > 0 then
table.insert(filtered_table, list_contents[i])
end
end
list_contents = filtered_table
end
if filter == 'titleonly' then
for i = 1, #list_contents do
if list_contents[i].found_title then
table.insert(filtered_table, list_contents[i])
end
end
list_contents = filtered_table
end
if filter == 'protocols' then
for i = 1, #list_contents do
if starts_protocol(o.logging_protocols, list_contents[i].found_path) then
table.insert(filtered_table, list_contents[i])
end
end
list_contents = filtered_table
end
if filter == 'keywords' then
for i = 1, #list_contents do
if contain_value(o.keywords_filter_list, list_contents[i].found_line) then
table.insert(filtered_table, list_contents[i])
end
end
list_contents = filtered_table
end
if filter == 'playing' then
for i = 1, #list_contents do
if list_contents[i].found_path == filePath then
table.insert(filtered_table, list_contents[i])
end
end
list_contents = filtered_table
end
if search_active and search_string ~= '' then
local filtered_table = {}
local search_query = ''
for search in search_string:gmatch("[^%s]+") do
search_query = search_query..'.-'..esc_string(search)
end
local contents_string = ''
for i = 1, #list_contents do
if o.search_behavior == 'specific' then
if string.lower(list_contents[i].found_path):match(string.lower(search_query)) then
table.insert(filtered_table, list_contents[i])
elseif list_contents[i].found_title and string.lower(list_contents[i].found_title):match(string.lower(search_query)) then
table.insert(filtered_table, list_contents[i])
elseif tonumber(list_contents[i].found_time) > 0 and format_time(list_contents[i].found_time, o.osd_time_format[3], o.osd_time_format[2], o.osd_time_format[1]):match(search_query) then
table.insert(filtered_table, list_contents[i])
elseif string.lower(list_contents[i].found_datetime):match(string.lower(search_query)) then
table.insert(filtered_table, list_contents[i])
end
elseif o.search_behavior == 'any' then
contents_string = list_contents[i].found_datetime..(list_contents[i].found_title or '')..list_contents[i].found_path
if tonumber(list_contents[i].found_time) > 0 then
contents_string = contents_string..format_time(list_contents[i].found_time, o.osd_time_format[3], o.osd_time_format[2], o.osd_time_format[1])
end
elseif o.search_behavior == 'any-notime' then
contents_string = list_contents[i].found_datetime..(list_contents[i].found_title or '')..list_contents[i].found_path
end
if string.lower(contents_string):match(string.lower(search_query)) then
table.insert(filtered_table, list_contents[i])
end
end
list_contents = filtered_table
end
if sort ~= current_sort then
list_sort(list_contents, sort)
end
if not list_contents and not search_active or not list_contents[1] and not search_active then return end
end
function get_list_sort(filter)
if not filter then filter = filterName end
local sort
for i=1, #o.list_filters_sort do
if o.list_filters_sort[i][1] == filter then
if has_value(available_sorts, o.list_filters_sort[i][2]) then sort = o.list_filters_sort[i][2] end
break
end
end
if not sort and has_value(available_sorts, o.list_default_sort) then sort = o.list_default_sort end
if not sort then sort = 'added-asc' end
return sort
end
function draw_list()
local osd_msg = ''
local osd_index = ''
local osd_key = ''
local osd_color = ''
local key = 0
local osd_text = string.format("{\\an%f{\\fscx%f}{\\fscy%f}{\\bord%f}{\\1c&H%s}", o.list_alignment, o.text_scale, o.text_scale, o.text_border, o.text_color)
local osd_cursor = string.format("{\\an%f}{\\fscx%f}{\\fscy%f}{\\bord%f}{\\1c&H%s}", o.list_alignment, o.text_cursor_scale, o.text_cursor_scale, o.text_cursor_border, o.text_cursor_color)
local osd_header = string.format("{\\an%f}{\\fscx%f}{\\fscy%f}{\\bord%f}{\\1c&H%s}", o.list_alignment, o.header_scale, o.header_scale, o.header_border, o.header_color)
local osd_msg_end = "{\\1c&HFFFFFF}"
local osd_time_type = 'found_time'
if o.text_time_type == 'length' then
osd_time_type = 'found_length'
elseif o.text_time_type == 'remaining' then
osd_time_type = 'found_remaining'
end
if o.header_text ~= '' then
osd_msg = osd_msg .. osd_header .. parse_header(o.header_text)
osd_msg = osd_msg .. "\\h\\N\\N" .. osd_msg_end
end
if search_active and not list_contents[1] then
osd_msg = osd_msg .. 'No search results found' .. osd_msg_end
end
if o.list_middle_loader then
list_start = list_cursor - math.floor(o.list_show_amount / 2)
else
list_start = list_cursor - o.list_show_amount
end
local showall = false
local showrest = false
if list_start < 0 then list_start = 0 end
if #list_contents <= o.list_show_amount then
list_start = 0
showall = true
end
if list_start > math.max(#list_contents - o.list_show_amount - 1, 0) then
list_start = #list_contents - o.list_show_amount
showrest = true
end
if list_start > 0 and not showall then
osd_msg = osd_msg .. o.list_sliced_prefix .. osd_msg_end
end
for i = list_start, list_start + o.list_show_amount - 1, 1 do
if i == #list_contents then break end
if o.show_paths then
p = list_contents[#list_contents - i].found_path or list_contents[#list_contents - i].found_name or ""
else
p = list_contents[#list_contents - i].found_name or list_contents[#list_contents - i].found_path or ""
end
if o.slice_longfilenames and p:len() > o.slice_longfilenames_amount then
p = p:sub(1, o.slice_longfilenames_amount) .. "..."
end
if o.quickselect_0to9_keybind and o.list_show_amount <= 10 and o.quickselect_0to9_pre_text then
key = 1 + key
if key == 10 then key = 0 end
osd_key = '(' .. key .. ') '
end
if o.show_item_number then
osd_index = (i + 1) .. '. '
end
if i + 1 == list_cursor then
osd_color = osd_cursor
else
osd_color = osd_text
end
for j = 1, #list_highlight_cursor do
if list_highlight_cursor[j] and list_highlight_cursor[j][1] == i+1 then
osd_msg = osd_msg..osd_color..esc_string(o.text_highlight_pre_text)
end
end
osd_msg = osd_msg .. osd_color .. osd_key .. osd_index .. p
if list_contents[#list_contents - i][osd_time_type] and tonumber(list_contents[#list_contents - i][osd_time_type]) > 0 then
osd_msg = osd_msg .. o.time_seperator .. format_time(list_contents[#list_contents - i][osd_time_type], o.list_time_format[3], o.list_time_format[2], o.list_time_format[1])
end
osd_msg = osd_msg .. '\\h\\N\\N' .. osd_msg_end
if i == list_start + o.list_show_amount - 1 and not showall and not showrest then
osd_msg = osd_msg .. o.list_sliced_suffix
end
end
mp.set_osd_ass(0, 0, osd_msg)
end
function list_empty_error_msg()
if list_contents ~= nil and list_contents[1] then return end
local msg_text
if filterName ~= 'all' then
msg_text = filterName .. " filter in History Empty"
else
msg_text = "History Empty"
end
msg.info(msg_text)
if o.osd_messages == true and not list_drawn then
mp.osd_message(msg_text)
end
end
function display_list(filter, sort, action)
if not filter or not has_value(available_filters, filter) then filter = 'all' end
if not sortName then sortName = get_list_sort(filter) end
local prev_sort = sortName
if not has_value(available_sorts, prev_sort) then prev_sort = get_list_sort() end
if not sort then sort = get_list_sort(filter) end
sortName = sort
local prev_filter = filterName
filterName = filter
get_list_contents(filter, sort)
if action ~= 'hide-osd' then
if not list_contents or not list_contents[1] then
list_empty_error_msg()
filterName = prev_filter
get_list_contents(filterName)
return
end
end
if not list_contents and not search_active or not list_contents[1] and not search_active then return end
if not has_value(o.filters_and_sequence, filter) then
table.insert(o.filters_and_sequence, filter)
end
local insert_new = false
local trigger_close_list = false
local trigger_initial_list = false
if not list_pages or not list_pages[1] then
table.insert(list_pages, {filter, 1, 1, {}, sort})
else
for i = 1, #list_pages do
if list_pages[i][1] == filter then
list_pages[i][3] = list_pages[i][3]+1
insert_new = false
break
else
insert_new = true
end
end
end
if insert_new then table.insert(list_pages, {filter, 1, 1, {}, sort}) end
for i = 1, #list_pages do
if not search_active and list_pages[i][1] == prev_filter then
list_pages[i][2] = list_cursor
list_pages[i][4] = list_highlight_cursor
list_pages[i][5] = prev_sort
end
if list_pages[i][1] ~= filter then
list_pages[i][3] = 0
end
if list_pages[i][3] == 2 and filter == 'all' and o.main_list_keybind_twice_exits then
trigger_close_list = true
elseif list_pages[i][3] == 2 and list_pages[1][1] == filter then
trigger_close_list = true
elseif list_pages[i][3] == 2 then
trigger_initial_list = true
end
end
if trigger_initial_list then
display_list(list_pages[1][1], nil, 'hide-osd')
return
end
if trigger_close_list then
list_close_and_trash_collection()
return
end
if not search_active then get_page_properties(filter) else update_search_results('','') end
draw_list()
if utils.shared_script_property_set then
utils.shared_script_property_set('simplehistory-menu-open', 'yes')
end
mp.set_property('user-data/simplehistory/menu-open', 'yes')
if o.toggle_idlescreen then mp.commandv('script-message', 'osc-idlescreen', 'no', 'no_osd') end --1.1.6# fix osc-idlescreen (value was yes for some reason)
list_drawn = true
if not search_active then get_list_keybinds() end
end
--End of LogManager (Read and Format the List from Log)--
--LogManager Navigation--
function select(pos, action)
if not search_active then
if not list_contents or not list_contents[1] then
list_close_and_trash_collection()
return
end
end
local list_cursor_temp = list_cursor + pos
if list_cursor_temp > 0 and list_cursor_temp <= #list_contents then
list_cursor = list_cursor_temp
if action == 'highlight' then
if not has_value(list_highlight_cursor, list_cursor, 1) then
if pos > -1 then
for i = pos, 1, -1 do
if not has_value(list_highlight_cursor, list_cursor-i, 1) then
table.insert(list_highlight_cursor, {list_cursor-i, list_contents[#list_contents+1+i - list_cursor]})
end
end
else
for i = pos, -1, 1 do
if not has_value(list_highlight_cursor, list_cursor-i, 1) then
table.insert(list_highlight_cursor, {list_cursor-i, list_contents[#list_contents+1+i - list_cursor]})
end
end
end
table.insert(list_highlight_cursor, {list_cursor, list_contents[#list_contents+1 - list_cursor]})
else
for i=1, #list_highlight_cursor do
if list_highlight_cursor[i] and list_highlight_cursor[i][1] == list_cursor then
table.remove(list_highlight_cursor, i)
end
end
if pos > -1 then
for i=1, #list_highlight_cursor do
for j = pos, 1, -1 do
if list_highlight_cursor[i] and list_highlight_cursor[i][1] == list_cursor-j then
table.remove(list_highlight_cursor, i)
end
end
end
else
for i=#list_highlight_cursor, 1, -1 do
for j = pos, -1, 1 do
if list_highlight_cursor[i] and list_highlight_cursor[i][1] == list_cursor-j then
table.remove(list_highlight_cursor, i)
end
end
end
end
end
end
end
if o.loop_through_list then
if list_cursor_temp > #list_contents then
list_cursor = 1
elseif list_cursor_temp < 1 then
list_cursor = #list_contents
end
end
draw_list()
end
function list_move_up(action)
select(-1, action)
if search_active and o.search_not_typing_smartly then
list_search_not_typing_mode(true)
end
end
function list_move_down(action)
select(1, action)
if search_active and o.search_not_typing_smartly then
list_search_not_typing_mode(true)
end
end
function list_move_first(action)
select(1 - list_cursor, action)
if search_active and o.search_not_typing_smartly then
list_search_not_typing_mode(true)
end
end
function list_move_last(action)
select(#list_contents - list_cursor, action)
if search_active and o.search_not_typing_smartly then
list_search_not_typing_mode(true)
end
end
function list_page_up(action)
select(list_start + 1 - list_cursor, action)
if search_active and o.search_not_typing_smartly then
list_search_not_typing_mode(true)
end
end
function list_page_down(action)
if o.list_middle_loader then
if #list_contents < o.list_show_amount then
select(#list_contents - list_cursor, action)
else
select(o.list_show_amount + list_start - list_cursor, action)
end
else
if o.list_show_amount > list_cursor then
select(o.list_show_amount - list_cursor, action)
elseif #list_contents - list_cursor >= o.list_show_amount then
select(o.list_show_amount, action)
else
select(#list_contents - list_cursor, action)
end
end
if search_active and o.search_not_typing_smartly then
list_search_not_typing_mode(true)
end
end
function list_highlight_all()
get_list_contents(filterName)
if not list_contents or not list_contents[1] then return end
if #list_highlight_cursor < #list_contents then
for i=1, #list_contents do
if not has_value(list_highlight_cursor, i, 1) then
table.insert(list_highlight_cursor, {i, list_contents[#list_contents+1-i]})
end
end
select(0)
else
list_unhighlight_all()
end
end
function list_unhighlight_all()
if not list_highlight_cursor or not list_highlight_cursor[1] then return end
list_highlight_cursor = {}
select(0)
end
--End of LogManager Navigation--
--LogManager Actions--
function load(list_cursor, add_playlist, target_time)
if not list_contents or not list_contents[1] then return end
if not target_time then
seekTime = tonumber(list_contents[#list_contents - list_cursor + 1].found_time) + o.resume_offset
if (seekTime < 0) then
seekTime = 0
end
else
seekTime = target_time
end
if file_exists(list_contents[#list_contents - list_cursor + 1].found_path) or starts_protocol(protocols, list_contents[#list_contents - list_cursor + 1].found_path) then
if not add_playlist then
if filePath ~= list_contents[#list_contents - list_cursor + 1].found_path then
mp.commandv('loadfile', list_contents[#list_contents - list_cursor + 1].found_path)
resume_selected = true
else
mp.commandv('seek', seekTime, 'absolute', 'exact')
list_close_and_trash_collection()
end
if o.osd_messages == true then
mp.osd_message('Loaded:\n' .. list_contents[#list_contents - list_cursor + 1].found_name.. o.time_seperator .. format_time(seekTime, o.osd_time_format[3], o.osd_time_format[2], o.osd_time_format[1]))
end
msg.info('Loaded the below file:\n' .. list_contents[#list_contents - list_cursor + 1].found_name .. ' | '.. format_time(seekTime))
else
mp.commandv('loadfile', list_contents[#list_contents - list_cursor + 1].found_path, 'append-play')
if o.osd_messages == true then
mp.osd_message('Added into Playlist:\n'..list_contents[#list_contents - list_cursor + 1].found_name..' ')
end
msg.info('Added the below file into playlist:\n' .. list_contents[#list_contents - list_cursor + 1].found_path)
end
else
if o.osd_messages == true then
mp.osd_message('File Doesn\'t Exist:\n' .. list_contents[#list_contents - list_cursor + 1].found_path)
end
msg.info('The file below doesn\'t seem to exist:\n' .. list_contents[#list_contents - list_cursor + 1].found_path)
return
end
end
function list_select()
load(list_cursor)
end
function list_add_playlist(action)
if not action then
load(list_cursor, true)
elseif action == 'highlight' then
if not list_highlight_cursor or not list_highlight_cursor[1] then return end
local file_ignored_total = 0
for i=1, #list_highlight_cursor do
if file_exists(list_highlight_cursor[i][2].found_path) or starts_protocol(protocols, list_highlight_cursor[i][2].found_path) then
mp.commandv("loadfile", list_highlight_cursor[i][2].found_path, "append-play")
else
msg.warn('The below file was not added into playlist as it does not seem to exist:\n' .. list_highlight_cursor[i][2].found_path)
file_ignored_total = file_ignored_total + 1
end
end
if o.osd_messages == true then
if file_ignored_total > 0 then
mp.osd_message('Added into Playlist '..#list_highlight_cursor - file_ignored_total..' Item/s\nIgnored '..file_ignored_total.. " Item/s That Do Not Exist")
else
mp.osd_message('Added into Playlist '..#list_highlight_cursor - file_ignored_total..' Item/s')
end
end
if file_ignored_total > 0 then
msg.warn('Ignored a total of '..file_ignored_total.. " Item/s that does not seem to exist")
end
msg.info('Added into playlist a total of '..#list_highlight_cursor - file_ignored_total..' item/s')
end
end
function delete_log_entry_specific(target_index, target_path, target_time)
local trigger_delete = false
list_contents = read_log_table()
if not list_contents or not list_contents[1] then return end
if target_index == 'last' then target_index = #list_contents end
if not target_index then return end
if target_index and target_path and target_time then
if list_contents[target_index].found_path == target_path and tonumber(list_contents[target_index].found_time) == target_time then
table.remove(list_contents, target_index)
trigger_delete = true
end
elseif target_index and target_path and not target_time then
if list_contents[target_index].found_path == target_path then
table.remove(list_contents, target_index)
trigger_delete = true
end
elseif target_index and target_time and not target_path then
if tonumber(list_contents[target_index].found_time) == target_time then
table.remove(list_contents, target_index)
trigger_delete = true
end
elseif target_index and not target_path and not target_time then
table.remove(list_contents, target_index)
trigger_delete = true
end
if not trigger_delete then return end
local f = io.open(log_fullpath, "w+")
if list_contents ~= nil and list_contents[1] then
for i = 1, #list_contents do
f:write(("%s\n"):format(list_contents[i].found_line))
end
end
f:close()
end
function delete_log_entry(multiple, round, target_path, target_time, entry_limit)
if not target_path then target_path = filePath end
if not target_time then target_time = seekTime end
list_contents = read_log_table()
if not list_contents or not list_contents[1] then return end
local trigger_delete = false
if not multiple then
for i = #list_contents, 1, -1 do
if not round then
if list_contents[i].found_path == target_path and tonumber(list_contents[i].found_time) == target_time then
table.remove(list_contents, i)
trigger_delete = true
break
end
else
if list_contents[i].found_path == target_path and math.floor(tonumber(list_contents[i].found_time)) == target_time then
table.remove(list_contents, i)
trigger_delete = true
break
end
end
end
else
for i = #list_contents, 1, -1 do
if not round then
if list_contents[i].found_path == target_path and tonumber(list_contents[i].found_time) == target_time then
table.remove(list_contents, i)
trigger_delete = true
end
else
if list_contents[i].found_path == target_path and math.floor(tonumber(list_contents[i].found_time)) == target_time then
table.remove(list_contents, i)
trigger_delete = true
end
end
end
end
if entry_limit and entry_limit > -1 then
local entries_found = 0
for i = #list_contents, 1, -1 do
if list_contents[i].found_path == target_path and entries_found < entry_limit then
entries_found = entries_found + 1
elseif list_contents[i].found_path == target_path and entries_found >= entry_limit then
table.remove(list_contents,i)
trigger_delete = true
end
end
end
if not trigger_delete then return end
local f = io.open(log_fullpath, "w+")
if list_contents ~= nil and list_contents[1] then
for i = 1, #list_contents do
f:write(("%s\n"):format(list_contents[i].found_line))
end
end
f:close()
end
function delete_log_entry_highlighted()
if not list_highlight_cursor or not list_highlight_cursor[1] then return end
list_contents = read_log_table()
if not list_contents or not list_contents[1] then return end
local list_contents_length = #list_contents
for i = 1, list_contents_length do
for j=1, #list_highlight_cursor do
if list_contents[list_contents_length+1-i] then
if list_contents[list_contents_length+1-i].found_sequence == list_highlight_cursor[j][2].found_sequence then
table.remove(list_contents, list_contents_length+1-i)
end
end
end
end
msg.info("Deleted "..#list_highlight_cursor.." Item/s")
list_unhighlight_all()
local f = io.open(log_fullpath, "w+")
if list_contents ~= nil and list_contents[1] then
for i = 1, #list_contents do
f:write(("%s\n"):format(list_contents[i].found_line))
end
end
f:close()
end
function delete_selected()
filePath = list_contents[#list_contents - list_cursor + 1].found_path
fileTitle = list_contents[#list_contents - list_cursor + 1].found_name
seekTime = tonumber(list_contents[#list_contents - list_cursor + 1].found_time)
if not filePath and not seekTime then
msg.info("Failed to delete")
return
end
delete_log_entry()
msg.info("Deleted \"" .. filePath .. "\" | " .. format_time(seekTime))
filePath, fileTitle, fileLength = get_file()
end
function list_delete(action)
if not action then
delete_selected()
elseif action == 'highlight' then
delete_log_entry_highlighted()
end
get_list_contents()
if not list_contents or not list_contents[1] then
list_close_and_trash_collection()
return
end
if list_cursor < #list_contents + 1 then
select(0)
else
list_move_last()
end
end
function get_total_duration(action)
if not list_contents or not list_contents[1] then return 0 end
local list_total_duration = 0
if action == 'found_time' or action == 'found_length' or action == 'found_remaining' then
for i = #list_contents, 1, -1 do
if tonumber(list_contents[i][action]) > 0 then
list_total_duration = list_total_duration + list_contents[i][action]
end
end
end
return list_total_duration
end
function list_cycle_sort()
local next_sort
for i = 1, #available_sorts do
if sortName == available_sorts[i] then
if i == #available_sorts then
next_sort = available_sorts[1]
break
else
next_sort = available_sorts[i+1]
break
end
end
end
if not next_sort then return end
get_list_contents(filterName, next_sort)
sortName = next_sort
update_list_highlist_cursor()
select(0)
end
function update_list_highlist_cursor()
if not list_highlight_cursor or not list_highlight_cursor[1] then return end
local temp_list_highlight_cursor = {}
for i = 1, #list_contents do
for j=1, #list_highlight_cursor do
if list_contents[#list_contents+1-i].found_sequence == list_highlight_cursor[j][2].found_sequence then
table.insert(temp_list_highlight_cursor, {i, list_highlight_cursor[j][2]})
end
end
end
list_highlight_cursor = temp_list_highlight_cursor
end
--End of LogManager Actions--
--LogManager Filter Functions--
function get_page_properties(filter)
if not filter then return end
for i=1, #list_pages do
if list_pages[i][1] == filter then
list_cursor = list_pages[i][2]
list_highlight_cursor = list_pages[i][4]
sortName = list_pages[i][5]
end
end
if list_cursor > #list_contents then
list_move_last()
end
end
function select_filter_sequence(pos)
if not list_drawn then return end
local curr_pos
local target_pos
for i = 1, #o.filters_and_sequence do
if filterName == o.filters_and_sequence[i] then
curr_pos = i
end
end
if curr_pos and pos > -1 then
for i = curr_pos, #o.filters_and_sequence do
if o.filters_and_sequence[i + pos] then
get_list_contents(o.filters_and_sequence[i + pos])
if list_contents ~= nil and list_contents[1] then
target_pos = i + pos
break
end
end
end
elseif curr_pos and pos < 0 then
for i = curr_pos, 0, -1 do
if o.filters_and_sequence[i + pos] then
get_list_contents(o.filters_and_sequence[i + pos])
if list_contents ~= nil and list_contents[1] then
target_pos = i + pos
break
end
end
end
end
if o.loop_through_filters then
if not target_pos and pos > -1 or target_pos and target_pos > #o.filters_and_sequence then
for i = 1, #o.filters_and_sequence do
get_list_contents(o.filters_and_sequence[i])
if list_contents ~= nil and list_contents[1] then
target_pos = i
break
end
end
end
if not target_pos and pos < 0 or target_pos and target_pos < 1 then
for i = #o.filters_and_sequence, 1, -1 do
get_list_contents(o.filters_and_sequence[i])
if list_contents ~= nil and list_contents[1] then
target_pos = i
break
end
end
end
end
if o.filters_and_sequence[target_pos] then
display_list(o.filters_and_sequence[target_pos], nil, 'hide-osd')
end
end
function list_filter_next()
select_filter_sequence(1)
end
function list_filter_previous()
select_filter_sequence(-1)
end
--End of LogManager Filter Functions--
--LogManager (List Bind and Unbind)--
function get_list_keybinds()
bind_keys(o.list_ignored_keybind, 'ignore')
bind_keys(o.list_move_up_keybind, 'move-up', list_move_up, 'repeatable')
bind_keys(o.list_move_down_keybind, 'move-down', list_move_down, 'repeatable')
bind_keys(o.list_move_first_keybind, 'move-first', list_move_first, 'repeatable')
bind_keys(o.list_move_last_keybind, 'move-last', list_move_last, 'repeatable')
bind_keys(o.list_page_up_keybind, 'page-up', list_page_up, 'repeatable')
bind_keys(o.list_page_down_keybind, 'page-down', list_page_down, 'repeatable')
bind_keys(o.list_select_keybind, 'list-select', list_select)
bind_keys(o.list_add_playlist_keybind, 'list-add-playlist', list_add_playlist)
bind_keys(o.list_add_playlist_highlighted_keybind, 'list-add-playlist-highlight', function()list_add_playlist('highlight')end)
bind_keys(o.list_delete_keybind, 'list-delete', list_delete)
bind_keys(o.list_delete_highlighted_keybind, 'list-delete-highlight', function()list_delete('highlight')end)
bind_keys(o.next_filter_sequence_keybind, 'list-filter-next', list_filter_next)
bind_keys(o.previous_filter_sequence_keybind, 'list-filter-previous', list_filter_previous)
bind_keys(o.list_search_activate_keybind, 'list-search-activate', list_search_activate)
bind_keys(o.list_highlight_all_keybind, 'list-highlight-all', list_highlight_all)
bind_keys(o.list_unhighlight_all_keybind, 'list-unhighlight-all', list_unhighlight_all)
bind_keys(o.list_cycle_sort_keybind, 'list-cycle-sort', list_cycle_sort)
for i = 1, #o.list_highlight_move_keybind do
for j = 1, #o.list_move_up_keybind do
mp.add_forced_key_binding(o.list_highlight_move_keybind[i]..'+'..o.list_move_up_keybind[j], 'highlight-move-up'..j, function()list_move_up('highlight') end, 'repeatable')
end
for j = 1, #o.list_move_down_keybind do
mp.add_forced_key_binding(o.list_highlight_move_keybind[i]..'+'..o.list_move_down_keybind[j], 'highlight-move-down'..j, function()list_move_down('highlight') end, 'repeatable')
end
for j = 1, #o.list_move_first_keybind do
mp.add_forced_key_binding(o.list_highlight_move_keybind[i]..'+'..o.list_move_first_keybind[j], 'highlight-move-first'..j, function()list_move_first('highlight') end, 'repeatable')
end
for j = 1, #o.list_move_last_keybind do
mp.add_forced_key_binding(o.list_highlight_move_keybind[i]..'+'..o.list_move_last_keybind[j], 'highlight-move-last'..j, function()list_move_last('highlight') end, 'repeatable')
end
for j = 1, #o.list_page_up_keybind do
mp.add_forced_key_binding(o.list_highlight_move_keybind[i]..'+'..o.list_page_up_keybind[j], 'highlight-page-up'..j, function()list_page_up('highlight') end, 'repeatable')
end
for j = 1, #o.list_page_down_keybind do
mp.add_forced_key_binding(o.list_highlight_move_keybind[i]..'+'..o.list_page_down_keybind[j], 'highlight-page-down'..j, function()list_page_down('highlight') end, 'repeatable')
end
end
if not search_active then
bind_keys(o.list_close_keybind, 'list-close', list_close_and_trash_collection)
end
for i = 1, #o.list_filter_jump_keybind do
mp.add_forced_key_binding(o.list_filter_jump_keybind[i][1], 'list-filter-jump'..i, function()display_list(o.list_filter_jump_keybind[i][2]) end)
end
for i = 1, #o.open_list_keybind do
if i == 1 then
mp.remove_key_binding('open-list')
else
mp.remove_key_binding('open-list'..i)
end
end
if o.quickselect_0to9_keybind and o.list_show_amount <= 10 then
mp.add_forced_key_binding("1", "recent-1", function()load(list_start + 1) end)
mp.add_forced_key_binding("2", "recent-2", function()load(list_start + 2) end)
mp.add_forced_key_binding("3", "recent-3", function()load(list_start + 3) end)
mp.add_forced_key_binding("4", "recent-4", function()load(list_start + 4) end)
mp.add_forced_key_binding("5", "recent-5", function()load(list_start + 5) end)
mp.add_forced_key_binding("6", "recent-6", function()load(list_start + 6) end)
mp.add_forced_key_binding("7", "recent-7", function()load(list_start + 7) end)
mp.add_forced_key_binding("8", "recent-8", function()load(list_start + 8) end)
mp.add_forced_key_binding("9", "recent-9", function()load(list_start + 9) end)
mp.add_forced_key_binding("0", "recent-0", function()load(list_start + 10) end)
end
end
function unbind_list_keys()
unbind_keys(o.list_ignored_keybind, 'ignore')
unbind_keys(o.list_move_up_keybind, 'move-up')
unbind_keys(o.list_move_down_keybind, 'move-down')
unbind_keys(o.list_move_first_keybind, 'move-first')
unbind_keys(o.list_move_last_keybind, 'move-last')
unbind_keys(o.list_page_up_keybind, 'page-up')
unbind_keys(o.list_page_down_keybind, 'page-down')
unbind_keys(o.list_select_keybind, 'list-select')
unbind_keys(o.list_add_playlist_keybind, 'list-add-playlist')
unbind_keys(o.list_add_playlist_highlighted_keybind, 'list-add-playlist-highlight')
unbind_keys(o.list_delete_keybind, 'list-delete')
unbind_keys(o.list_delete_highlighted_keybind, 'list-delete-highlight')
unbind_keys(o.list_close_keybind, 'list-close')
unbind_keys(o.next_filter_sequence_keybind, 'list-filter-next')
unbind_keys(o.previous_filter_sequence_keybind, 'list-filter-previous')
unbind_keys(o.list_highlight_all_keybind, 'list-highlight-all')
unbind_keys(o.list_highlight_all_keybind, 'list-unhighlight-all')
unbind_keys(o.list_cycle_sort_keybind, 'list-cycle-sort')
for i = 1, #o.list_move_up_keybind do
mp.remove_key_binding('highlight-move-up'..i)
end
for i = 1, #o.list_move_down_keybind do
mp.remove_key_binding('highlight-move-down'..i)
end
for i = 1, #o.list_move_first_keybind do
mp.remove_key_binding('highlight-move-first'..i)
end
for i = 1, #o.list_move_last_keybind do
mp.remove_key_binding('highlight-move-last'..i)
end
for i = 1, #o.list_page_up_keybind do
mp.remove_key_binding('highlight-page-up'..i)
end
for i = 1, #o.list_page_down_keybind do
mp.remove_key_binding('highlight-page-down'..i)
end
for i = 1, #o.list_filter_jump_keybind do
mp.remove_key_binding('list-filter-jump'..i)
end
for i = 1, #o.open_list_keybind do
if i == 1 then
mp.add_forced_key_binding(o.open_list_keybind[i][1], 'open-list', function()display_list(o.open_list_keybind[i][2]) end)
else
mp.add_forced_key_binding(o.open_list_keybind[i][1], 'open-list'..i, function()display_list(o.open_list_keybind[i][2]) end)
end
end
if o.quickselect_0to9_keybind and o.list_show_amount <= 10 then
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")
end
end
function list_close_and_trash_collection()
if utils.shared_script_property_set then
utils.shared_script_property_set('simplehistory-menu-open', 'no')
end
mp.set_property('user-data/simplehistory/menu-open', 'no')
if o.toggle_idlescreen then mp.commandv('script-message', 'osc-idlescreen', 'yes', 'no_osd') end
unbind_list_keys()
unbind_search_keys()
mp.set_osd_ass(0, 0, "")
list_drawn = false
list_cursor = 1
list_start = 0
filterName = 'all'
list_pages = {}
search_string = ''
search_active = false
list_highlight_cursor = {}
sortName = nil
end
--End of LogManager (List Bind and Unbind)--
--LogManager Search Feature--
function list_search_exit()
search_active = false
get_list_contents(filterName)
get_page_properties(filterName)
select(0)
unbind_search_keys()
get_list_keybinds()
end
function list_search_not_typing_mode(auto_triggered)
if auto_triggered then
if search_string ~= '' and list_contents[1] then
search_active = 'not_typing'
elseif not list_contents[1] then
return
else
search_active = false
end
else
if search_string ~= '' then
search_active = 'not_typing'
else
search_active = false
end
end
select(0)
unbind_search_keys()
get_list_keybinds()
end
function list_search_activate()
if not list_drawn then return end
if search_active == 'typing' then list_search_exit() return end
search_active = 'typing'
for i = 1, #list_pages do
if list_pages[i][1] == filterName then
list_pages[i][2] = list_cursor
list_pages[i][4] = list_highlight_cursor
list_pages[i][5] = sortName
end
end
update_search_results('','')
bind_search_keys()
end
function update_search_results(character, action)
if not character then character = '' end
if action == 'string_del' then
search_string = search_string:sub(1, -2)
end
search_string = search_string..character
local prev_contents_length = #list_contents
get_list_contents(filterName)
if prev_contents_length ~= #list_contents then
list_highlight_cursor = {}
end
if character ~= '' and #list_contents > 0 or action ~= nil and #list_contents > 0 then
select(1-list_cursor)
elseif #list_contents == 0 then
list_cursor = 0
select(list_cursor)
else
select(0)
end
end
function bind_search_keys()
mp.add_forced_key_binding('a', 'search_string_a', function() update_search_results('a') end, 'repeatable')
mp.add_forced_key_binding('b', 'search_string_b', function() update_search_results('b') end, 'repeatable')
mp.add_forced_key_binding('c', 'search_string_c', function() update_search_results('c') end, 'repeatable')
mp.add_forced_key_binding('d', 'search_string_d', function() update_search_results('d') end, 'repeatable')
mp.add_forced_key_binding('e', 'search_string_e', function() update_search_results('e') end, 'repeatable')
mp.add_forced_key_binding('f', 'search_string_f', function() update_search_results('f') end, 'repeatable')
mp.add_forced_key_binding('g', 'search_string_g', function() update_search_results('g') end, 'repeatable')
mp.add_forced_key_binding('h', 'search_string_h', function() update_search_results('h') end, 'repeatable')
mp.add_forced_key_binding('i', 'search_string_i', function() update_search_results('i') end, 'repeatable')
mp.add_forced_key_binding('j', 'search_string_j', function() update_search_results('j') end, 'repeatable')
mp.add_forced_key_binding('k', 'search_string_k', function() update_search_results('k') end, 'repeatable')
mp.add_forced_key_binding('l', 'search_string_l', function() update_search_results('l') end, 'repeatable')
mp.add_forced_key_binding('m', 'search_string_m', function() update_search_results('m') end, 'repeatable')
mp.add_forced_key_binding('n', 'search_string_n', function() update_search_results('n') end, 'repeatable')
mp.add_forced_key_binding('o', 'search_string_o', function() update_search_results('o') end, 'repeatable')
mp.add_forced_key_binding('p', 'search_string_p', function() update_search_results('p') end, 'repeatable')
mp.add_forced_key_binding('q', 'search_string_q', function() update_search_results('q') end, 'repeatable')
mp.add_forced_key_binding('r', 'search_string_r', function() update_search_results('r') end, 'repeatable')
mp.add_forced_key_binding('s', 'search_string_s', function() update_search_results('s') end, 'repeatable')
mp.add_forced_key_binding('t', 'search_string_t', function() update_search_results('t') end, 'repeatable')
mp.add_forced_key_binding('u', 'search_string_u', function() update_search_results('u') end, 'repeatable')
mp.add_forced_key_binding('v', 'search_string_v', function() update_search_results('v') end, 'repeatable')
mp.add_forced_key_binding('w', 'search_string_w', function() update_search_results('w') end, 'repeatable')
mp.add_forced_key_binding('x', 'search_string_x', function() update_search_results('x') end, 'repeatable')
mp.add_forced_key_binding('y', 'search_string_y', function() update_search_results('y') end, 'repeatable')
mp.add_forced_key_binding('z', 'search_string_z', function() update_search_results('z') end, 'repeatable')
mp.add_forced_key_binding('A', 'search_string_A', function() update_search_results('A') end, 'repeatable')
mp.add_forced_key_binding('B', 'search_string_B', function() update_search_results('B') end, 'repeatable')
mp.add_forced_key_binding('C', 'search_string_C', function() update_search_results('C') end, 'repeatable')
mp.add_forced_key_binding('D', 'search_string_D', function() update_search_results('D') end, 'repeatable')
mp.add_forced_key_binding('E', 'search_string_E', function() update_search_results('E') end, 'repeatable')
mp.add_forced_key_binding('F', 'search_string_F', function() update_search_results('F') end, 'repeatable')
mp.add_forced_key_binding('G', 'search_string_G', function() update_search_results('G') end, 'repeatable')
mp.add_forced_key_binding('H', 'search_string_H', function() update_search_results('H') end, 'repeatable')
mp.add_forced_key_binding('I', 'search_string_I', function() update_search_results('I') end, 'repeatable')
mp.add_forced_key_binding('J', 'search_string_J', function() update_search_results('J') end, 'repeatable')
mp.add_forced_key_binding('K', 'search_string_K', function() update_search_results('K') end, 'repeatable')
mp.add_forced_key_binding('L', 'search_string_L', function() update_search_results('L') end, 'repeatable')
mp.add_forced_key_binding('M', 'search_string_M', function() update_search_results('M') end, 'repeatable')
mp.add_forced_key_binding('N', 'search_string_N', function() update_search_results('N') end, 'repeatable')
mp.add_forced_key_binding('O', 'search_string_O', function() update_search_results('O') end, 'repeatable')
mp.add_forced_key_binding('P', 'search_string_P', function() update_search_results('P') end, 'repeatable')
mp.add_forced_key_binding('Q', 'search_string_Q', function() update_search_results('Q') end, 'repeatable')
mp.add_forced_key_binding('R', 'search_string_R', function() update_search_results('R') end, 'repeatable')
mp.add_forced_key_binding('S', 'search_string_S', function() update_search_results('S') end, 'repeatable')
mp.add_forced_key_binding('T', 'search_string_T', function() update_search_results('T') end, 'repeatable')
mp.add_forced_key_binding('U', 'search_string_U', function() update_search_results('U') end, 'repeatable')
mp.add_forced_key_binding('V', 'search_string_V', function() update_search_results('V') end, 'repeatable')
mp.add_forced_key_binding('W', 'search_string_W', function() update_search_results('W') end, 'repeatable')
mp.add_forced_key_binding('X', 'search_string_X', function() update_search_results('X') end, 'repeatable')
mp.add_forced_key_binding('Y', 'search_string_Y', function() update_search_results('Y') end, 'repeatable')
mp.add_forced_key_binding('Z', 'search_string_Z', function() update_search_results('Z') end, 'repeatable')
mp.add_forced_key_binding('1', 'search_string_1', function() update_search_results('1') end, 'repeatable')
mp.add_forced_key_binding('2', 'search_string_2', function() update_search_results('2') end, 'repeatable')
mp.add_forced_key_binding('3', 'search_string_3', function() update_search_results('3') end, 'repeatable')
mp.add_forced_key_binding('4', 'search_string_4', function() update_search_results('4') end, 'repeatable')
mp.add_forced_key_binding('5', 'search_string_5', function() update_search_results('5') end, 'repeatable')
mp.add_forced_key_binding('6', 'search_string_6', function() update_search_results('6') end, 'repeatable')
mp.add_forced_key_binding('7', 'search_string_7', function() update_search_results('7') end, 'repeatable')
mp.add_forced_key_binding('8', 'search_string_8', function() update_search_results('8') end, 'repeatable')
mp.add_forced_key_binding('9', 'search_string_9', function() update_search_results('9') end, 'repeatable')
mp.add_forced_key_binding('0', 'search_string_0', function() update_search_results('0') end, 'repeatable')
mp.add_forced_key_binding('SPACE', 'search_string_space', function() update_search_results(' ') end, 'repeatable')
mp.add_forced_key_binding('`', 'search_string_`', function() update_search_results('`') end, 'repeatable')
mp.add_forced_key_binding('~', 'search_string_~', function() update_search_results('~') end, 'repeatable')
mp.add_forced_key_binding('!', 'search_string_!', function() update_search_results('!') end, 'repeatable')
mp.add_forced_key_binding('@', 'search_string_@', function() update_search_results('@') end, 'repeatable')
mp.add_forced_key_binding('SHARP', 'search_string_sharp', function() update_search_results('#') end, 'repeatable')
mp.add_forced_key_binding('$', 'search_string_$', function() update_search_results('$') end, 'repeatable')
mp.add_forced_key_binding('%', 'search_string_percentage', function() update_search_results('%') end, 'repeatable')
mp.add_forced_key_binding('^', 'search_string_^', function() update_search_results('^') end, 'repeatable')
mp.add_forced_key_binding('&', 'search_string_&', function() update_search_results('&') end, 'repeatable')
mp.add_forced_key_binding('*', 'search_string_*', function() update_search_results('*') end, 'repeatable')
mp.add_forced_key_binding('(', 'search_string_(', function() update_search_results('(') end, 'repeatable')
mp.add_forced_key_binding(')', 'search_string_)', function() update_search_results(')') end, 'repeatable')
mp.add_forced_key_binding('-', 'search_string_-', function() update_search_results('-') end, 'repeatable')
mp.add_forced_key_binding('_', 'search_string__', function() update_search_results('_') end, 'repeatable')
mp.add_forced_key_binding('=', 'search_string_=', function() update_search_results('=') end, 'repeatable')
mp.add_forced_key_binding('+', 'search_string_+', function() update_search_results('+') end, 'repeatable')
mp.add_forced_key_binding('\\', 'search_string_\\', function() update_search_results('\\') end, 'repeatable')
mp.add_forced_key_binding('|', 'search_string_|', function() update_search_results('|') end, 'repeatable')
mp.add_forced_key_binding(']', 'search_string_]', function() update_search_results(']') end, 'repeatable')
mp.add_forced_key_binding('}', 'search_string_rightcurly', function() update_search_results('}') end, 'repeatable')
mp.add_forced_key_binding('[', 'search_string_[', function() update_search_results('[') end, 'repeatable')
mp.add_forced_key_binding('{', 'search_string_leftcurly', function() update_search_results('{') end, 'repeatable')
mp.add_forced_key_binding('\'', 'search_string_\'', function() update_search_results('\'') end, 'repeatable')
mp.add_forced_key_binding('\"', 'search_string_\"', function() update_search_results('\"') end, 'repeatable')
mp.add_forced_key_binding(';', 'search_string_semicolon', function() update_search_results(';') end, 'repeatable')
mp.add_forced_key_binding(':', 'search_string_:', function() update_search_results(':') end, 'repeatable')
mp.add_forced_key_binding('/', 'search_string_/', function() update_search_results('/') end, 'repeatable')
mp.add_forced_key_binding('?', 'search_string_?', function() update_search_results('?') end, 'repeatable')
mp.add_forced_key_binding('.', 'search_string_.', function() update_search_results('.') end, 'repeatable')
mp.add_forced_key_binding('>', 'search_string_>', function() update_search_results('>') end, 'repeatable')
mp.add_forced_key_binding(',', 'search_string_,', function() update_search_results(',') end, 'repeatable')
mp.add_forced_key_binding('<', 'search_string_<', function() update_search_results('<') end, 'repeatable')
mp.add_forced_key_binding('bs', 'search_string_del', function() update_search_results('', 'string_del') end, 'repeatable')
bind_keys(o.list_close_keybind, 'search_exit', function() list_search_exit() end)
bind_keys(o.list_search_not_typing_mode_keybind, 'search_string_not_typing', function()list_search_not_typing_mode(false) end)
if o.search_not_typing_smartly then
bind_keys(o.next_filter_sequence_keybind, 'list-filter-next', function() list_filter_next() list_search_not_typing_mode(true) end)
bind_keys(o.previous_filter_sequence_keybind, 'list-filter-previous', function() list_filter_previous() list_search_not_typing_mode(true) end)
bind_keys(o.list_delete_keybind, 'list-delete', function() list_delete() list_search_not_typing_mode(true) end)
bind_keys(o.list_delete_highlighted_keybind, 'list-delete-highlight', function() list_delete('highlight') list_search_not_typing_mode(true) end)
end
end
function unbind_search_keys()
mp.remove_key_binding('search_string_a')
mp.remove_key_binding('search_string_b')
mp.remove_key_binding('search_string_c')
mp.remove_key_binding('search_string_d')
mp.remove_key_binding('search_string_e')
mp.remove_key_binding('search_string_f')
mp.remove_key_binding('search_string_g')
mp.remove_key_binding('search_string_h')
mp.remove_key_binding('search_string_i')
mp.remove_key_binding('search_string_j')
mp.remove_key_binding('search_string_k')
mp.remove_key_binding('search_string_l')
mp.remove_key_binding('search_string_m')
mp.remove_key_binding('search_string_n')
mp.remove_key_binding('search_string_o')
mp.remove_key_binding('search_string_p')
mp.remove_key_binding('search_string_q')
mp.remove_key_binding('search_string_r')
mp.remove_key_binding('search_string_s')
mp.remove_key_binding('search_string_t')
mp.remove_key_binding('search_string_u')
mp.remove_key_binding('search_string_v')
mp.remove_key_binding('search_string_w')
mp.remove_key_binding('search_string_x')
mp.remove_key_binding('search_string_y')
mp.remove_key_binding('search_string_z')
mp.remove_key_binding('search_string_A')
mp.remove_key_binding('search_string_B')
mp.remove_key_binding('search_string_C')
mp.remove_key_binding('search_string_D')
mp.remove_key_binding('search_string_E')
mp.remove_key_binding('search_string_F')
mp.remove_key_binding('search_string_G')
mp.remove_key_binding('search_string_H')
mp.remove_key_binding('search_string_I')
mp.remove_key_binding('search_string_J')
mp.remove_key_binding('search_string_K')
mp.remove_key_binding('search_string_L')
mp.remove_key_binding('search_string_M')
mp.remove_key_binding('search_string_N')
mp.remove_key_binding('search_string_O')
mp.remove_key_binding('search_string_P')
mp.remove_key_binding('search_string_Q')
mp.remove_key_binding('search_string_R')
mp.remove_key_binding('search_string_S')
mp.remove_key_binding('search_string_T')
mp.remove_key_binding('search_string_U')
mp.remove_key_binding('search_string_V')
mp.remove_key_binding('search_string_W')
mp.remove_key_binding('search_string_X')
mp.remove_key_binding('search_string_Y')
mp.remove_key_binding('search_string_Z')
mp.remove_key_binding('search_string_1')
mp.remove_key_binding('search_string_2')
mp.remove_key_binding('search_string_3')
mp.remove_key_binding('search_string_4')
mp.remove_key_binding('search_string_5')
mp.remove_key_binding('search_string_6')
mp.remove_key_binding('search_string_7')
mp.remove_key_binding('search_string_8')
mp.remove_key_binding('search_string_9')
mp.remove_key_binding('search_string_0')
mp.remove_key_binding('search_string_space')
mp.remove_key_binding('search_string_`')
mp.remove_key_binding('search_string_~')
mp.remove_key_binding('search_string_!')
mp.remove_key_binding('search_string_@')
mp.remove_key_binding('search_string_sharp')
mp.remove_key_binding('search_string_$')
mp.remove_key_binding('search_string_percentage')
mp.remove_key_binding('search_string_^')
mp.remove_key_binding('search_string_&')
mp.remove_key_binding('search_string_*')
mp.remove_key_binding('search_string_(')
mp.remove_key_binding('search_string_)')
mp.remove_key_binding('search_string_-')
mp.remove_key_binding('search_string__')
mp.remove_key_binding('search_string_=')
mp.remove_key_binding('search_string_+')
mp.remove_key_binding('search_string_\\')
mp.remove_key_binding('search_string_|')
mp.remove_key_binding('search_string_]')
mp.remove_key_binding('search_string_rightcurly')
mp.remove_key_binding('search_string_[')
mp.remove_key_binding('search_string_leftcurly')
mp.remove_key_binding('search_string_\'')
mp.remove_key_binding('search_string_\"')
mp.remove_key_binding('search_string_semicolon')
mp.remove_key_binding('search_string_:')
mp.remove_key_binding('search_string_/')
mp.remove_key_binding('search_string_?')
mp.remove_key_binding('search_string_.')
mp.remove_key_binding('search_string_>')
mp.remove_key_binding('search_string_,')
mp.remove_key_binding('search_string_<')
mp.remove_key_binding('search_string_del')
if not search_active then
unbind_keys(o.list_close_keybind, 'search_exit')
end
end
--End of LogManager Search Feature--
---------End of LogManager---------
function history_blacklist_check()
if not o.history_blacklist[1] or #o.history_blacklist == 1 and o.history_blacklist[1] == "" then return false end
local invertable_return = {true, false}
local blacklist_msg = 'File was not added to history because of blacklist'
if o.invert_history_blacklist then
invertable_return = {false, true}
blacklist_msg = 'File was added to history because of whitelist'
end
if has_value(o.history_blacklist, filePath, nil) then
msg.info(blacklist_msg)
return invertable_return[1]
elseif not starts_protocol(protocols, filePath) then
if has_value(o.history_blacklist, filePath:match('^(.-)([^\\/]-)%.([^\\/%.]-)%.?$'), nil) or
has_value(o.history_blacklist, filePath:match('^(.-)([^\\/]-)%.([^\\/%.]-)%.?$'):gsub('\\$', ''), nil) then
msg.info(blacklist_msg)
return invertable_return[1]
elseif has_value(o.history_blacklist, filePath:match('%.([^%.]+)$'), nil) or
has_value(o.history_blacklist, "."..filePath:match('%.([^%.]+)$'), nil) then
msg.info(blacklist_msg)
return invertable_return[1]
else --1.1.2# check to add any subfolder after /* to blacklist. issue #70
for i=1, #o.history_blacklist do --1.1.2# loop through blacklisted items, if the blacklist ends with * and it is a match after subbing of the current filePath then log it. #and additionally if it is the exact same path then ignore it.
if string.lower(filePath):match(string.lower(o.history_blacklist[i])) and o.history_blacklist[i]:sub(-1,#o.history_blacklist[i]) == '*' and string.lower(o.history_blacklist[i]:sub(1,-2)) ~= string.lower(filePath):match("(.*[\\/])") then
msg.info(blacklist_msg)
return invertable_return[1]
end
end
end
elseif starts_protocol(protocols, filePath) then
if has_value(o.history_blacklist, filePath:match('(.-)(:)'), nil) or
has_value(o.history_blacklist, filePath:match('(.-:)'), nil) or
has_value(o.history_blacklist, filePath:match('(.-:/?/?)'), nil) then
msg.info(blacklist_msg)
return invertable_return[1]
elseif filePath:find('https?://') == 1 then
local difchk_1, difchk_2 = filePath:match("(https?://)w?w?w?%.?([%w%.%:]*)")
local different_check_temp = difchk_1..difchk_2
local different_checks = {different_check_temp, filePath:match("https?://w?w?w?%.?([%w%.%:]*)"), filePath:match("https?://([%w%.%:]*)"), filePath:match("(https?://[%w%.%:]*)") }
for i = 1, #different_checks do
if different_checks[i] and has_value(o.history_blacklist, different_checks[i], nil)
or different_checks[i]..'/' and has_value(o.history_blacklist, different_checks[i]..'/', nil) then
msg.info(blacklist_msg)
return invertable_return[1]
end
end
end
end
return invertable_return[2]
end
function mark_chapter()
if not o.mark_history_as_chapter then return end
local all_chapters = mp.get_property_native("chapter-list")
local chapter_index = 0
local chapters_time = {}
get_list_contents()
if not list_contents or not list_contents[1] then return end
for i = 1, #list_contents do
if list_contents[i].found_path == filePath and tonumber(list_contents[i].found_time) > 0 then
table.insert(chapters_time, tonumber(list_contents[i].found_time))
end
end
if not chapters_time[1] then return end
table.sort(chapters_time, function(a, b) return a < b end)
for i = 1, #chapters_time do
chapter_index = chapter_index + 1
all_chapters[chapter_index] = {
title = 'SimpleHistory ' .. chapter_index,
time = chapters_time[i]
}
end
table.sort(all_chapters, function(a, b) return a['time'] < b['time'] end)
mp.set_property_native("chapter-list", all_chapters)
end
function write_log(target_time, update_seekTime, entry_limit)
if not filePath then return end
local prev_seekTime = seekTime
seekTime = (mp.get_property_number('time-pos') or 0)
if target_time then
seekTime = target_time
end
if seekTime < 0 then seekTime = 0 end
delete_log_entry(false, true, filePath, math.floor(seekTime), entry_limit)
local f = io.open(log_fullpath, "a+")
if o.file_title_logging == 'all' then
f:write(("[%s] \"%s\" | %s | %s | %s"):format(os.date(o.date_format), fileTitle, filePath, log_length_text .. tostring(fileLength), log_time_text .. tostring(seekTime)))
elseif o.file_title_logging == 'protocols' and (starts_protocol(o.logging_protocols, filePath)) then
f:write(("[%s] \"%s\" | %s | %s | %s"):format(os.date(o.date_format), fileTitle, filePath, log_length_text .. tostring(fileLength), log_time_text .. tostring(seekTime)))
elseif o.file_title_logging == 'protocols' and not (starts_protocol(o.logging_protocols, filePath)) then
f:write(("[%s] %s | %s | %s"):format(os.date(o.date_format), filePath, log_length_text .. tostring(fileLength), log_time_text .. tostring(seekTime)))
else
f:write(("[%s] %s | %s | %s"):format(os.date(o.date_format), filePath, log_length_text .. tostring(fileLength), log_time_text .. tostring(seekTime)))
end
f:write('\n')
f:close()
if not update_seekTime then
seekTime = prev_seekTime
end
end
function history_incognito_mode()
if not incognito_mode then
incognito_mode = true
if o.osd_messages == true then
mp.osd_message('🕵 Incognito Mode Enabled')
end
msg.info('Incognito Mode Enabled')
if o.delete_incognito_entry and autosaved_entry == true then
delete_log_entry_specific('last', filePath, 0)
autosaved_entry = 'autosaved-restore'
if list_drawn then
get_list_contents()
select(0)
end
end
else
incognito_mode = false
if o.osd_messages == true then
mp.osd_message('Incognito Mode Disabled')
end
msg.info('Incognito Mode Disabled')
if o.restore_incognito_entry == 'always' then
history_fileonly_save()
autosaved_entry = true
elseif o.restore_incognito_entry == 'deleted-restore' and autosaved_entry == 'autosaved-restore' then
history_fileonly_save()
autosaved_entry = true
if list_drawn then
get_list_contents()
select(0)
end
end
end
end
function history_resume_option()
if o.resume_option == 'notification' or o.resume_option == 'force' then
local video_time = mp.get_property_number('time-pos')
local video_path = mp.get_property('path') --1.1.4# local variable instead of filePath
if video_time > 0 then return end
local logged_time = 0
local percentage = 0
local video_duration = (mp.get_property_number('duration') or 0)
list_contents = read_log_table()
if not list_contents or not list_contents[1] then return end
for i = #list_contents, 1, -1 do
if list_contents[i].found_path == video_path and tonumber(list_contents[i].found_time) > 0 then --1.1.4# instead of filePath in case it is causing issue
logged_time = tonumber(list_contents[i].found_time) + o.resume_offset
break
end
end
if logged_time > 0 then
percentage = math.floor((logged_time / video_duration) * 100 + 0.5)
if o.resume_option == 'notification' then
if percentage > o.resume_option_threshold and percentage < (100-o.resume_option_threshold) or o.resume_option_threshold == 0 then
mp.osd_message('⌨ [' .. string.upper(o.history_resume_keybind[1]) .. '] Resumes To' .. o.time_seperator .. format_time(logged_time, o.osd_time_format[3], o.osd_time_format[2], o.osd_time_format[1]),3)
end
elseif o.resume_option == 'force' then
if percentage > o.resume_option_threshold and percentage < (100-o.resume_option_threshold) or o.resume_option_threshold == 0 then
mp.commandv('seek', logged_time, 'absolute', 'exact')
if (o.osd_messages == true) then
mp.osd_message('Resumed To Last Played Position\n' .. o.time_seperator .. format_time(logged_time, o.osd_time_format[3], o.osd_time_format[2], o.osd_time_format[1]))
end
msg.info('Resumed to the last played position')
end
end
end
end
end
function history_save(target_time)
if filePath ~= nil then
if history_blacklist_check() then
return
end
write_log(target_time, false, o.same_entry_limit)
if list_drawn then
get_list_contents()
select(0)
end
msg.info('Added the below into history\n' .. fileTitle .. o.time_seperator .. format_time(seekTime))
else
msg.info("Failed to add into history")
end
end
function history_fileonly_save()
if filePath ~= nil then
if history_blacklist_check() then
return
end
write_log(0, false)
if list_drawn then
get_list_contents()
select(0)
end
msg.info('Added the below into history\n' .. fileTitle .. o.time_seperator .. format_time(seekTime))
else
msg.info("Failed to add into history, no file found")
end
end
function history_resume()
if filePath == nil then
list_contents = read_log_table()
load(1)
elseif filePath ~= nil then
list_contents = read_log_table()
if list_contents ~= nil and list_contents[1] then
for i = #list_contents, 1, -1 do
if list_contents[i].found_path == filePath and tonumber(list_contents[i].found_time) > 0 then
seekTime = tonumber(list_contents[i].found_time) + o.resume_offset
break
end
end
end
if seekTime > 0 then
mp.commandv('seek', seekTime, 'absolute', 'exact')
if (o.osd_messages == true) then
mp.osd_message('Resumed To Last Played Position\n' .. o.time_seperator .. format_time(seekTime, o.osd_time_format[3], o.osd_time_format[2], o.osd_time_format[1]))
end
msg.info('Resumed to the last played position')
else
if (o.osd_messages == true) then
mp.osd_message('No Resume Position Found For This Video')
end
msg.info('No resume position found for this video')
end
end
end
function history_load_last()
if filePath == nil then
list_contents = read_log_table()
load(1, false, 0)
elseif filePath ~= nil then
list_contents = read_log_table()
load(2, true)
end
end
mp.register_event('file-loaded', function()
list_close_and_trash_collection()
filePath, fileTitle, fileLength = get_file()
loadTriggered = true --1.1.5# for resume and resume-notime startup behavior (so that it only triggers if started as idle and only once)
if (resume_selected == true and seekTime > 0) then
mp.commandv('seek', seekTime, 'absolute', 'exact')
resume_selected = false
end
history_resume_option() --1.1.4# remove timeout, cant remember why I put it in first place
mark_chapter()
if not incognito_mode then
history_fileonly_save()
autosaved_entry = true
end
end)
mp.add_hook('on_unload', 9, function()--1.1.3# get the LogTime only when using on_unload because big functions do not run fully in here
logTime = (mp.get_property_number('time-pos') or 0)
end)
mp.register_event('end-file', function()--1.1.3# use end-file instead so that it doesn't cause crash while seeking ( i am able to run big functions here)
if not incognito_mode then
if autosaved_entry == true then delete_log_entry_specific('last', filePath, 0) end
history_save(logTime) --1.1.3# get the updated time from on_unload since it will still be preserved
end
autosaved_entry = false
logTime = 0 --1.1.3# reset logTime to 0
end)
mp.observe_property("idle-active", "bool", function(_, v)
if v then --1.1.2# if idle is triggered
filePath, fileTitle, fileLength = nil --1.1.2# set it back to nil if idle is triggered for better trash collection. issue #69
end
if v and o.startup_idle_behavior == 'resume' and not loadTriggered then --1.1.5# option to resume on startup
history_resume()
elseif v and o.startup_idle_behavior == 'resume-notime' and not loadTriggered then --1.1.5# option to load last item on startup
history_load_last()
elseif v and has_value(available_filters, o.auto_run_list_idle) then
display_list(o.auto_run_list_idle, nil, 'hide-osd')
end
if v and o.auto_run_incognito_mode and not incognito_auto_run_triggered or
not v and o.auto_run_incognito_mode and not incognito_auto_run_triggered then
history_incognito_mode()
incognito_auto_run_triggered = true
end
end)
bind_keys(o.history_resume_keybind, 'history-resume', history_resume)
bind_keys(o.history_load_last_keybind, 'history-load-last', history_load_last)
bind_keys(o.history_incognito_mode_keybind, 'history-incognito-mode', history_incognito_mode)
for i = 1, #o.open_list_keybind do
if i == 1 then
mp.add_forced_key_binding(o.open_list_keybind[i][1], 'open-list', function()display_list(o.open_list_keybind[i][2]) end)
else
mp.add_forced_key_binding(o.open_list_keybind[i][1], 'open-list'..i, function()display_list(o.open_list_keybind[i][2]) end)
end
end