diff options
Diffstat (limited to 'mac/.config/mpv/scripts/SimpleBookmark.lua')
| -rw-r--r-- | mac/.config/mpv/scripts/SimpleBookmark.lua | 2907 |
1 files changed, 2907 insertions, 0 deletions
diff --git a/mac/.config/mpv/scripts/SimpleBookmark.lua b/mac/.config/mpv/scripts/SimpleBookmark.lua new file mode 100644 index 0000000..5af7f74 --- /dev/null +++ b/mac/.config/mpv/scripts/SimpleBookmark.lua @@ -0,0 +1,2907 @@ +-- Copyright (c) 2023, Eisa AlAwadhi +-- License: BSD 2-Clause License +-- Creator: Eisa AlAwadhi +-- Project: SimpleBookmark +-- Version: 1.3.1 + +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---- + --Available filters: 'all', 'keybinds', 'groups', 'recents', 'distinct', 'protocols', 'fileonly', 'titleonly', 'timeonly', 'keywords'. + --Filters description: "all" to display all the items. Or 'groups' to display the list filtered with items added to any group. Or 'keybinds' to display the list filtered with keybind slots. 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 can also be stacked by using %+% or omitted by using %-%. e.g.: "groups%+%keybinds" shows only groups and keybinds, "all%-%groups%-%keybinds" shows all items without groups and without keybinds. + --Also defined groups can be called by using /:group%Group Name% + auto_run_list_idle = 'none', --Auto run the list when opening mpv and there is no video / file loaded. none for disabled. Or choose between available filters. + load_item_on_startup = 0, --runs a saved entry when mpv starts based on its number. -1 for oldest entry, 1 for latest entry, or select the number to load a specific entry, 0 for disabled + toggle_idlescreen = true, --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 + bookmark_loads_last_idle = true, --When attempting to bookmark, if there is no video / file loaded, it will instead jump to your last bookmarked item and resume it. + bookmark_fileonly_loads_last_idle = true, --When attempting to bookmark fileonly, if there is no video / file loaded, it will instead jump to your last bookmarked item without resuming. + mark_bookmark_as_chapter = false, --true is for marking the time as a chapter. false disables mark as chapter behavior. + preserve_video_settings = false, --(true/false). Preserve video settings when bookmarking items and loading bookmarks by writing mpv watch-later config + bookmark_save_keybind=[[ + ["ctrl+b", "ctrl+B"] + ]], --Keybind that will be used to save the video and its time to log file + bookmark_fileonly_keybind=[[ + ["alt+b", "alt+B"] + ]], --Keybind that will be used to save the video without time to log file + open_list_keybind=[[ + [ ["b", "all"], ["B", "all"], ["k", "keybinds"], ["K", "keybinds"] ] + ]], --Keybind that will be used to open the list along with the specified filter. + list_filter_jump_keybind=[[ + [ ["b", "all"], ["B", "all"], ["k", "keybinds"], ["K", "keybinds"], ["!", "/:group%TV Shows%"], ["@", "/:group%Movies%"], ["SHARP", "/:group%Anime%"], ["$", "/:group%Anime Movies%"], ["%", "/:group%Cartoon%"], ["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', 'keybinds', 'recents', 'distinct', 'protocols', 'fileonly', 'titleonly', 'timeonly', 'keywords'. + + -----Keybind Slots Settings----- + keybinds_quicksave_fileonly = true, --When quick saving to a keybind slot, it will not save position + keybinds_empty_auto_create = false, --If the keybind slot is empty, this enables quick logging and adding to slot, Otherwise keybinds are assigned from the list or via quicksave. + keybinds_empty_fileonly = true, --When auto creating keybind slot, it will not save position. + keybinds_auto_resume = true, --When loading a keybind slot, it will auto resume to the saved time. + keybinds_add_load_keybind=[[ + ["alt+1", "alt+2", "alt+3", "alt+4", "alt+5", "alt+6", "alt+7", "alt+8", "alt+9"] + ]], --Keybind that will be used to bind list item to a key, as well as to load it. e.g.: Press alt+1 on list cursor position to add it, press alt+1 while list is hidden to load item keybinded into alt+1. (A new slot is automatically created for each keybind. e.g: .."alt+9, alt+0". Where alt+0 creates a new 10th slot.) + keybinds_quicksave_keybind=[[ + ["alt+!", "alt+@", "alt+#", "alt+$", "alt+%", "alt+^", "alt+&", "alt+*", "alt+("] + ]], --To save keybind to a slot without opening the list, to load these keybinds it uses keybinds_add_load_keybind + keybinds_remove_keybind=[[ + ["alt+-"] + ]], --Keybind that is used when list is open to remove the keybind slot based on cursor position + keybinds_remove_highlighted_keybind=[[ + ["alt+_"] + ]], --Keybind that is used when list is open to remove the keybind slot based on highlighted items + + -----Group Settings----- + groups_list_and_keybind =[[ + [ ["TV Shows", "ctrl+1", "ctrl+!"], ["Movies", "ctrl+2", "ctrl+@"], ["Anime", "ctrl+3", "ctrl+#"], ["Anime Movies", "ctrl+4", "ctrl+$"], ["Cartoon", "ctrl+5"], ["Animated Movies"] ] + ]], --Define the groups that can be assigned to a bookmarked item, you can also optionally assign the keybind, and the highlight keybind that puts the bookmarked item into the relevant group when the list is open. Alternatively you can use list_group_add_cycle_keybind to assign item to a group + list_groups_remove_keybind=[[ + ["ctrl+-"] + ]], --Keybind that is used when list is open to remove the group based on cursor position + list_groups_remove_highlighted_keybind=[[ + ["ctrl+_"] + ]], --Keybind that is used when list is open to remove the group based on highlighted items + list_group_add_cycle_keybind=[[ + ["ctrl+g"] + ]], --Keybind to add an item to the group, this cycles through all the different available groups when list is open + list_group_add_cycle_highlighted_keybind=[[ + ["ctrl+G"] + ]], --Keybind to add highlighted items to the group, this cycles through all the different available groups when list is open + + -----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 = 'mpvBookmark.log', --name+extension of the file that will be used to store the log data + file_title_logging = 'all', --Change between 'all', 'protocols', 'local', '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') + same_entry_limit = -1, --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. + overwrite_preserve_properties = true, --true is to preserve groups / slots or any other property when an entry is overwritten. + + -----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. + quickselect_0to9_keybind = false, --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', --'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------ + filters_and_sequence=[[ + ["all", "keybinds", "groups", "/:group%TV Shows%", "/:group%Movies%", "/:group%Anime%", "/:group%Anime Movies%", "/:group%Cartoon%", "/:group%Animated Movies%", "protocols", "fileonly", "titleonly", "timeonly", "playing", "keywords", "recents", "distinct", "keybinds%+%groups", "all%-%groups%-%keybinds"] + ]], --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"]. To disable this filter keep it empty [] + + -----Sort Settings------ + --Available sorts: 'added-asc', 'added-desc', 'time-asc', 'time-desc', 'alphanum-asc', 'alphanum-desc' + --Sorts description: '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. Choose between available sorts. + list_filters_sort=[[ + [ ["keybinds", "keybind-asc"], ["fileonly", "alphanum-asc"], ["playing", "time-asc"] ] + ]], --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. + slice_name = false, --Change to true or false. Slices long names per the amount specified below + slice_name_amount = 55, --Amount for slicing long names (for path, name, and title) list_content_text variables + list_show_amount = 10, --Change maximum number to show items at once + list_content_text = '%number%. %name%%0_duration%%duration%%0_keybind%%keybind%%0_group%%group%%1_group%\\h\\N\\N', --Text to be shown as header for the list + --list_content_text variables: %quickselect%, %number%, %name%, %title%, %path%, %duration%, %length%, %remaining%, %dt%, %dt_"format%"% + --Variables explanation: %quickselect%: keybind for quickselect. %number%: numbered sequence of the item position. %name%: shows the file name. %title%: shows file title. %path%: shows the filepath or url. %duration%: the reached playback time of item. %length%: the total time length of the file. %remaining% the remaining playback time of file. %dt%: the logged date and time. + --You can also use %dt_"format%"%" as per lua date formatting (https://www.lua.org/pil/22.1.html). It is specified after dt_ ..example: (%dt_%a% %dt_%b% %dt_%y%) for abbreviated day month year + list_content_variables=[[ + [ ["0_duration", " 🕒 "], ["0_keybind", " ⌨ "], ["0_group", " 🖿 "] ] + ]], --User defined variables that only displays if the related variable is triggered. + --#_group, #_keybind, #_duration, #_length, #_remaining, #_dt. (# represents the possibility of creating many variables using different numbers. e.g.: "0_keybind", "1_keybind") + 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. + 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 = '00bfff', --Search color when in typing mode + search_color_not_typing = 'ffffaa', --Search color when not in typing mode and it is active + header_color = 'ffffaa', --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 = '🔖 Bookmarks [%cursor%/%total%]%0_highlight%%highlight%%0_filter%%filter%%1_filter%%0_sort%%sort%%1_sort%%0_search%%search%%1_search%\\h\\N\\N', --The formatting of the items when you open the list + --header_text variables: %cursor%, %total%, %highlight%, %filter%, %search%, %duration%, %length%, %remaining%. + --Variables explanation: %cursor%: the number of cursor position. %total%: total amount in current list. %highlight%: total number of highlighted items. %filter%: shows the filter name, %search%: shows the typed search. %duration%: the total reached playback time of all displayed items. %length%: the total time length of the file for all displayed items. %remaining% the remaining playback time of file for all the displayed items. + header_variables=[[ + [ ["0_highlight", "✅"], ["0_filter", " [Filter: "], ["1_filter", "]"], ["0_sort", " \\{"], ["1_sort", "}"], ["0_search", "\\h\\N\\N[Search="], ["1_search", "..]"] ] + ]], --User defined variables that only displays if the related variable is triggered. + --#_filter, #_sort, #_highlight, #_search, #_duration, #_length%, #_remaining. (# represents the possibility of creating many variables using different numbers. e.g.: "0_filter", "1_filter") + header_sort_hide_text = 'added-asc',--Sort method that is hidden from header when using %sort% variable + + -----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_duration_time_format=[[ + ["default", "truncate"] + ]], + list_length_time_format=[[ + ["default", "truncate"] + ]], + list_remaining_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=[[ + ["h", "H", "r", "R", "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.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.osd_time_format = utils.parse_json(o.osd_time_format) +o.list_duration_time_format = utils.parse_json(o.list_duration_time_format)--1.3# changed and added time format for each in the list +o.list_length_time_format = utils.parse_json(o.list_length_time_format)--1.3# added time format for each in the list +o.list_remaining_time_format = utils.parse_json(o.list_remaining_time_format)--1.3# added time format for each in the list +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.bookmark_save_keybind = utils.parse_json(o.bookmark_save_keybind) +o.bookmark_fileonly_keybind = utils.parse_json(o.bookmark_fileonly_keybind) +o.keybinds_add_load_keybind = utils.parse_json(o.keybinds_add_load_keybind) +o.keybinds_remove_keybind = utils.parse_json(o.keybinds_remove_keybind) +o.keybinds_remove_highlighted_keybind = utils.parse_json(o.keybinds_remove_highlighted_keybind) +o.keybinds_quicksave_keybind = utils.parse_json(o.keybinds_quicksave_keybind) +o.groups_list_and_keybind = utils.parse_json(o.groups_list_and_keybind) +o.list_groups_remove_keybind = utils.parse_json(o.list_groups_remove_keybind) +o.list_groups_remove_highlighted_keybind = utils.parse_json(o.list_groups_remove_highlighted_keybind) +o.list_group_add_cycle_keybind = utils.parse_json(o.list_group_add_cycle_keybind) +o.list_group_add_cycle_highlighted_keybind = utils.parse_json(o.list_group_add_cycle_highlighted_keybind) +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_content_variables = utils.parse_json(o.list_content_variables)--1.3# for new config +o.header_variables = utils.parse_json(o.header_variables)--1.3# for new config +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('simplebookmark-menu-open', 'no') +end +mp.set_property('user-data/simplebookmark/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 log_keybind_text = 'slot=' +local log_group_text = 'group=' +local protocols = {'https?:', 'magnet:', 'rtmps?:', 'smb:', 'ftps?:', 'sftp:'} +local available_sorts = {'added-asc', 'added-desc', 'time-asc', 'time-desc', 'alphanum-asc', 'alphanum-desc'} +local search_string = '' +local search_active = false +local loadTriggered = false --1.3.0# to identify if load is triggered atleast once for idle option +local resume_selected = false +local osd_log_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 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() --1.3# removed prefer filename overtitle + 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("\"", "") + + return path, title, length +end + +function get_local_names(target, property) --1.3# function to get names and fall back to whatever is found --1.2.4# removed or "" so that I can check for errors if the returned value is nil + local target_filename = target.found_name or target.found_title or target.found_path + local target_filepath = target.found_path or target.found_name or target.found_title + local target_filetitle = target.found_title or target.found_name or target.found_path + if not property then + return target_filename, target_filepath, target_filetitle + elseif property == 'osd' then --1.3# added osd property so it removes special character functions when displaying osd in mpv (uses gsub from search) + return esc_ass(target_filename), esc_ass(target_filepath), esc_ass(target_filetitle) + end +end + +function get_slot_keybind(keyindex) + local keybind_return + + if o.keybinds_add_load_keybind[keyindex] then + keybind_return = o.keybinds_add_load_keybind[keyindex] + else + keybind_return = log_keybind_text .. (keyindex or '') .. ' undefined' + end + + return keybind_return +end + +function get_group_properties(groupindex, action) + local gname, gkeybind, ghkeybind + + if o.groups_list_and_keybind[groupindex] and o.groups_list_and_keybind[groupindex][1] then + gname = o.groups_list_and_keybind[groupindex][1] + else + gname = log_group_text ..(groupindex or '').. ' undefined' + end + + if o.groups_list_and_keybind[groupindex] and o.groups_list_and_keybind[groupindex][2] then + gkeybind = o.groups_list_and_keybind[groupindex][2] + else + gkeybind = log_group_text ..(groupindex or '').. ' undefined' + end + + if o.groups_list_and_keybind[groupindex] and o.groups_list_and_keybind[groupindex][3] then + ghkeybind = o.groups_list_and_keybind[groupindex][3] + else + ghkeybind = log_group_text ..(groupindex or '').. ' undefined' + end + + return {name = gname, keybind = gkeybind, highlight_keybind = ghkeybind} +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 + +function esc_ass(str) --1.3# used function to escape, also this function will use the byte order mark instead of immediately pasting the zero-width space + return str:gsub('\\', '\\\239\187\191'):gsub('{', '\\{') +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 = {} + local line_count = 0 + 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, g + if line:match('^.-\"(.-)\"') then --1.3# changed if statement to only get title and path + tt, p = line:match('^.-\"(.-)\" | (.*) | ' .. esc_string(log_length_text) .. '(.*)') + else --1.3# get path only if no title is there + p = line:match('[(.*)%]]%s(.*) | ' .. esc_string(log_length_text) .. '(.*)') + end + d, n, e = p:match('^(.-)([^\\/]-)%.([^\\/%.]-)%.?$') --1.3# not inside if statement anymore since we are not changing name with title anymore + dt = line:match('%[(.-)%]') + t = line:match(' | ' .. esc_string(log_time_text) .. '(%d*%.?%d*)(.*)$') + ln = line:match(' | ' .. esc_string(log_length_text) .. '(%d*%.?%d*)(.*)$') + if tonumber(ln) and tonumber(t) then r = tonumber(ln) - tonumber(t) else r = 0 end + s = line:match(' | .* | ' .. esc_string(log_keybind_text) .. '(%d*)(.*)$') + g = line:match(' | .* | ' .. esc_string(log_group_text) .. '(%d*)(.*)$') + 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, found_slot = s, found_group = g} + 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 == 'keybind-asc' and filterName == 'keybinds' then + table.sort(tab, function(a, b) return a['found_slot'] > b['found_slot'] end) + elseif sort == 'keybind-desc' and filterName == 'keybinds' then + table.sort(tab, function(a, b) return a['found_slot'] < b['found_slot'] 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 get_o_variable(str, arr_var) --1.3# function to get variable content from passed array + if not str then return end + if str:match('%%(.*)%%') then str = str:match('%%(.*)%%') end --1.3# if the entry has % around it, then remove it + + local var_return + for i = 1, #arr_var do --1.3# loop through the passed array and get the value of the matched variable + if arr_var[i][1] == str then + var_return = arr_var[i][2] --1.3# added or "" so if that the content of the variable is not defined it does not crash + break + end + end + + return var_return or "" --1.3# return the founded variable content or empty string if nothing is found + +end + +function parse_list_item(str, properties) --1.3#add ability to parse the contents of the list like the header + if not str then return msg.error('str in parse_list_item is nil') end + + local list_filename, list_filepath, list_filetitle = get_local_names(properties["item"],'osd')--1.3# added osd property so it removes special characters for displaying list + + if o.slice_name and list_filepath:len() > o.slice_name_amount then --1.3.1# fix #86 since p doesn't exist anymore, and checks for specific filename / filepath / filetitle, so slicing is accurate. + list_filepath = list_filepath:sub(1, o.slice_name_amount) .. "..." + end + if o.slice_name and list_filename:len() > o.slice_name_amount then + list_filename = list_filename:sub(1, o.slice_name_amount) .. "..." + end + if o.slice_name and list_filetitle:len() > o.slice_name_amount then + list_filetitle = list_filetitle:sub(1, o.slice_name_amount) .. "..." + end + + str = str:gsub("%%name%%", list_filename) + :gsub("%%path%%", list_filepath) + :gsub("%%title%%", list_filetitle) + :gsub("%%number%%", properties["index"]+1) --1.3# index +1 is the osd_index + :gsub("%%dt%%", properties["item"].found_datetime) + + for s in str:gmatch("%%dt_%%.%%") do --1.3# loop through all found dt_ in the script a + local svar = s:match("_%%."):sub(2) --1.3# match whatever starting from _ when using %dt_var%, then sub(2) to remove the first letter which is _ (then var will remain) to use in our gsub + if parse_8601(properties["item"].found_datetime) then --1.3.1# for backward compatibility if matching did not work reset to null + str = str:gsub(esc_string(s), os.date(svar, parse_8601(properties["item"].found_datetime))) --1.3# replaces the found dt_var with eg. dt_%a from config + + for x in str:gmatch("%%%d_dt%%") do --1.3.1# for backward compatibility adds 0_dt to be able to force customize date and time variables + str = str:gsub(esc_string(x), get_o_variable(x, o.list_content_variables)) + end + else --1.3.1# for backward compatibility if matching did not work reset to null + str = str:gsub(esc_string(s), "") + + for x in str:gmatch("%%%d_dt%%") do --1.3.1# for backward compatibility removes 0_dt if log time cannot be parsed from in log + str = str:gsub(esc_string(x), "") + end + end + end + + if properties["item"].found_slot then + str = str:gsub("%%keybind%%", get_slot_keybind(tonumber(properties["item"].found_slot))) + for s in str:gmatch("%%%d*_keybind%%") do --1.3# if a custom group variable is found, such as %0_group% then get the content of the variable for it + str = str:gsub(esc_string(s), get_o_variable(s, o.list_content_variables)) + end + else + str = str:gsub("%%keybind%%", "") + for s in str:gmatch("%%%d*_keybind%%") do --1.3# if a custom slot is found and there is no slot assigned, remove it + str = str:gsub(esc_string(s), "") + end + end + + if properties["item"].found_group then + str = str:gsub("%%group%%", get_group_properties(tonumber(properties["item"].found_group)).name) + for s in str:gmatch("%%%d*_group%%") do --1.3# if a custom group variable is found, such as %0_group% then get the content of the variable for it + str = str:gsub(esc_string(s), get_o_variable(s, o.list_content_variables)) + end + else + str = str:gsub("%%group%%", "") + for s in str:gmatch("%%%d*_group%%") do + str = str:gsub(esc_string(s), "") + end + end + + if properties['quickselect'] and str:match("%%quickselect%%") then --1.2# replace quickselect with the actual key if its available + str = str:gsub("%%quickselect%%", properties['quickselect']) + else + str = str:gsub("%%quickselect%%", "") + end + + --1.3# same concept for showing groups but for time + if properties["item"].found_time and tonumber(properties["item"].found_time) > 0 then + str = str:gsub('%%duration%%', format_time(properties["item"].found_time, o.list_duration_time_format[3], o.list_duration_time_format[2], o.list_duration_time_format[1])) + for s in str:gmatch("%%%d*_duration%%") do + str = str:gsub(esc_string(s), get_o_variable(s, o.list_content_variables)) + end + else + str = str:gsub("%%duration%%", "") + for s in str:gmatch("%%%d*_duration%%") do + str = str:gsub(esc_string(s), "") + end + end + if properties["item"].found_length and tonumber(properties["item"].found_length) > 0 then + str = str:gsub('%%length%%', format_time(properties["item"].found_length, o.list_length_time_format[3], o.list_length_time_format[2], o.list_length_time_format[1])) + for s in str:gmatch("%%%d*_length%%") do + str = str:gsub(esc_string(s), get_o_variable(s, o.list_content_variables)) + end + else + str = str:gsub("%%length%%", "") + for s in str:gmatch("%%%d*_length%%") do + str = str:gsub(esc_string(s), "") + end + end + if properties["item"].found_remaining and tonumber(properties["item"].found_remaining) > 0 then + str = str:gsub('%%remaining%%', format_time(properties["item"].found_remaining, o.list_remaining_time_format[3], o.list_remaining_time_format[2], o.list_remaining_time_format[1])) + for s in str:gmatch("%%%d*_remaining%%") do + str = str:gsub(esc_string(s), get_o_variable(s, o.list_content_variables)) + end + else + str = str:gsub("%%remaining%%", "") + for s in str:gmatch("%%%d*_remaining%%") do + str = str:gsub(esc_string(s), "") + end + end + str = str:gsub("%%%%", "%%") + + return str +end + +function parse_header(str) + 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 + + str = str:gsub("%%total%%", #osd_log_contents) + :gsub("%%cursor%%", list_cursor) + + local filter_osd = filterName + if filter_osd ~= 'all' then + if filter_osd:match('/:group%%(.*)%%') then filter_osd = filter_osd:match('/:group%%(.*)%%') end + str = str:gsub("%%filter%%", filter_osd) + for s in str:gmatch("%%%d*_filter%%") do --1.3# if a filter variable is found, such as %0_filter% then get the content of the variable for it + str = str:gsub(esc_string(s), get_o_variable(s, o.header_variables)) + end + else + str = str:gsub("%%filter%%", '') + for s in str:gmatch("%%%d*_filter%%") do --1.3# if a custom slot is found and there is no slot assigned, remove it + str = str:gsub(esc_string(s), "") + end + end + + if str:match('%%duration%%') then + if get_total_duration('found_time') > 0 then + str = str:gsub("%%duration%%", format_time(get_total_duration('found_time'), o.header_duration_time_format[3], o.header_duration_time_format[2], o.header_duration_time_format[1])) + for s in str:gmatch("%%%d*_duration%%") do --1.3# if a filter variable is found, such as %0_filter% then get the content of the variable for it + str = str:gsub(esc_string(s), get_o_variable(s, o.header_variables)) + end + else + str = str:gsub("%%duration%%", '') + for s in str:gmatch("%%%d*_duration%%") do --1.3# if a custom slot is found and there is no slot assigned, remove it + str = str:gsub(esc_string(s), "") + end + end + end + + if str:match('%%length%%') then + if get_total_duration('found_length') > 0 then + str = str:gsub("%%length%%", format_time(get_total_duration('found_length'), o.header_length_time_format[3], o.header_length_time_format[2], o.header_length_time_format[1])) + for s in str:gmatch("%%%d*_length%%") do --1.3# if a filter variable is found, such as %0_filter% then get the content of the variable for it + str = str:gsub(esc_string(s), get_o_variable(s, o.header_variables)) + end + else + str = str:gsub("%%length%%", '') + for s in str:gmatch("%%%d*_length%%") do --1.3# if a filter variable is found, such as %0_filter% then get the content of the variable for it + str = str:gsub(esc_string(s), "") + end + end + end + + if str:match('%remaining%%') then + if get_total_duration('found_remaining') > 0 then + str = str:gsub("%%remaining%%", format_time(get_total_duration('found_remaining'), o.header_remaining_time_format[3], o.header_remaining_time_format[2], o.header_remaining_time_format[1])) + for s in str:gmatch("%%%d*_remaining%%") do --1.3# if a filter variable is found, such as %0_filter% then get the content of the variable for it + str = str:gsub(esc_string(s), get_o_variable(s, o.header_variables)) + end + else + str = str:gsub("%%remaining%%", '') + for s in str:gmatch("%%%d*_remaining%%") do --1.3# if a filter variable is found, such as %0_filter% then get the content of the variable for it + str = str:gsub(esc_string(s), "") + end + end + end + + if #list_highlight_cursor > 0 then + str = str:gsub("%%highlight%%", #list_highlight_cursor) + for s in str:gmatch("%%%d*_highlight%%") do --1.3# if a filter variable is found, such as %0_filter% then get the content of the variable for it + str = str:gsub(esc_string(s), get_o_variable(s, o.header_variables)) + end + else + str = str:gsub("%%highlight%%", '') + for s in str:gmatch("%%%d*_highlight%%") do --1.3# if a filter variable is found, such as %0_filter% then get the content of the variable for it + str = str:gsub(esc_string(s), "") + end + end + + if sortName and sortName ~= o.header_sort_hide_text then + str = str:gsub("%%sort%%", sortName) + for s in str:gmatch("%%%d*_sort%%") do --1.3# if a filter variable is found, such as %0_filter% then get the content of the variable for it + str = str:gsub(esc_string(s), get_o_variable(s, o.header_variables)) + end + else + str = str:gsub("%%sort%%", '') + for s in str:gmatch("%%%d*_sort%%") do --1.3# if a filter variable is found, such as %0_filter% then get the content of the variable for it + str = str:gsub(esc_string(s), "") + end + end + + if search_active then + local search_string_osd = search_string + if search_string_osd ~= '' then + search_string_osd = esc_ass(search_string:gsub('%%', '%%%%%%%%')) --1.3# used ass_escape instead of gsub + end + + str = str:gsub("%%search%%", osd_search_color..search_string_osd..osd_header_color) + for s in str:gmatch("%%%d*_search%%") do --1.3# if a filter variable is found, such as %0_filter% then get the content of the variable for it + str = str:gsub(esc_string(s), get_o_variable(s, o.header_variables)) + end + else + str = str:gsub("%%search%%", '') + for s in str:gmatch("%%%d*_search%%") do --1.3# if a filter variable is found, such as %0_filter% then get the content of the variable for it + str = str:gsub(esc_string(s), "") + end + end + str = str:gsub("%%%%", "%%") + return str +end + +function search_log_contents(arr_contents) + if not arr_contents or not arr_contents[1] or not search_active or not search_string == '' then return false end + + local search_query = '' + for search in search_string:gmatch("[^%s]+") do + search_query = search_query..'.-'..esc_string(search) + end + local contents_string = '' + + local search_arr_contents = {} + + for i = 1, #arr_contents do --1.3# removed specific search method as it doesn't seem useful anymore, --1.3.1# utilize arr_contents instead of osd_log_contents + if o.search_behavior == 'any' then + contents_string = arr_contents[i].found_datetime --1.3# seperated date and time for search + if parse_8601(arr_contents[i].found_datetime) then --1.3# an if statement to check if date could be parsed --1.3# allows for all type of dates to be searched thanks to the loop + local os_date_tag= {'%a', '%A', '%b', '%B', '%c', '%d', '%H', '%I', '%M', '%m', '%p', '%S', '%w', '%x', '%X', '%Y', '%y'} --1.3# add all lua date time parameters + for j=1, #os_date_tag do --1.3# replace all lua parameters with date values that can be searched + contents_string = contents_string..os.date(os_date_tag[j], parse_8601(arr_contents[i].found_datetime)) + end + end + contents_string = contents_string..(arr_contents[i].found_title or '')..(arr_contents[i].found_name or '')..arr_contents[i].found_path --1.3# added found_name since parsing is different now + if tonumber(arr_contents[i].found_time) > 0 then + contents_string = contents_string..format_time(arr_contents[i].found_time, o.list_duration_time_format[3], o.list_duration_time_format[2], o.list_duration_time_format[1]) + end + if tonumber(arr_contents[i].found_length) > 0 then + contents_string = contents_string..format_time(arr_contents[i].found_length, o.list_length_time_format[3], o.list_length_time_format[2], o.list_length_time_format[1]) + end + if tonumber(arr_contents[i].found_remaining) > 0 then + contents_string = contents_string..format_time(arr_contents[i].found_remaining, o.list_remaining_time_format[3], o.list_remaining_time_format[2], o.list_remaining_time_format[1]) + end + if arr_contents[i].found_slot then + contents_string = contents_string..get_slot_keybind(tonumber(arr_contents[i].found_slot)) + end + if arr_contents[i].found_group then + contents_string = contents_string..get_group_properties(tonumber(arr_contents[i].found_group)).name + end + elseif o.search_behavior == 'any-notime' then + contents_string = arr_contents[i].found_datetime --1.3# seperated date and time for search + if parse_8601(arr_contents[i].found_datetime) then --1.3# an if statement to check if date could be parsed + local os_date_tag= {'%a', '%A', '%b', '%B', '%c', '%d', '%H', '%I', '%M', '%m', '%p', '%S', '%w', '%x', '%X', '%Y', '%y'} --1.3# add all lua date time parameters + for j=1, #os_date_tag do --1.3# replace all lua parameters with values that can be searched + contents_string = contents_string..os.date(os_date_tag[j], parse_8601(arr_contents[i].found_datetime)) + end + end + contents_string = contents_string..(arr_contents[i].found_title or '')..(arr_contents[i].found_name or '')..arr_contents[i].found_path --1.3# added found_name since parsing is different now + if arr_contents[i].found_slot then + contents_string = contents_string..get_slot_keybind(tonumber(arr_contents[i].found_slot)) + end + if arr_contents[i].found_group then + contents_string = contents_string..get_group_properties(tonumber(arr_contents[i].found_group)).name + end + end + + if string.lower(contents_string):match(string.lower(search_query)) then + table.insert(search_arr_contents, arr_contents[i]) + end + end + + return search_arr_contents + +end + +function filter_log_contents(arr_contents, filter) + if not arr_contents or not arr_contents[1] or not filter or filter == 'all' then return false end + local filtered_arr_contents = {} + + if filter:match('%%%+%%') then + if filter_stack(arr_contents,filter) then filtered_arr_contents = filter_stack(arr_contents, filter) end + elseif filter:match('%%%-%%') then + if filter_omit(arr_contents,filter) then filtered_arr_contents = filter_omit(arr_contents, filter) end + else + if filter_apply(arr_contents, filter) then filtered_arr_contents = filter_apply(arr_contents, filter) end + end + + return filtered_arr_contents +end + + +function filter_omit(arr_contents, filter) + if not arr_contents or not arr_contents[1] or not filter or filter == 'all' or not filter:match('%%%-%%') then return false end + local omitted_arr_table = arr_contents + + local filter_items = {} + for f in filter:gmatch("[^%%%-%%\r+]+") do + table.insert(filter_items, f) + end + + local temp_filtered_contents = arr_contents + for i=1, #filter_items do + if i== 1 and filter_apply(arr_contents, filter_items[i]) then omitted_arr_table = filter_apply(arr_contents, filter_items[i]) end + if i > 1 then + if filter_apply(arr_contents, filter_items[i]) then temp_filtered_contents = filter_apply(arr_contents, filter_items[i]) end + for j=1, #temp_filtered_contents do + for k=1, #omitted_arr_table do + if temp_filtered_contents[j] and omitted_arr_table[k] and temp_filtered_contents[j].found_sequence == omitted_arr_table[k].found_sequence then + table.remove(omitted_arr_table, k) + end + end + end + end + end + + table.sort(omitted_arr_table, function(a, b) return a['found_sequence'] < b['found_sequence'] end) + + return omitted_arr_table +end + +function filter_stack(arr_contents, filter) + if not arr_contents or not arr_contents[1] or not filter or filter == 'all' or not filter:match('%%%+%%') then return false end + local stacked_arr_table = {} + local filter_items = {} + + for f in filter:gmatch("[^%%%+%%\r+]+") do + table.insert(filter_items, f) + end + + local unique_values = {} + local temp_filtered_contents = arr_contents + for i=1, #filter_items do + if filter_apply(arr_contents, filter_items[i]) then temp_filtered_contents = filter_apply(arr_contents, filter_items[i]) end + for j=1, #temp_filtered_contents do + if not has_value(unique_values, temp_filtered_contents[j].found_sequence) then + table.insert(unique_values, temp_filtered_contents[j].found_sequence) + table.insert(stacked_arr_table, temp_filtered_contents[j]) + end + end + end + table.sort(stacked_arr_table, function(a, b) return a['found_sequence'] < b['found_sequence'] end) + + return stacked_arr_table + +end + +function filter_apply(arr_contents, filter) + if not arr_contents or not arr_contents[1] or not filter or filter == 'all' then return false end + local filtered_arr_contents = {} + + if filter == 'groups' then + for i = 1, #arr_contents do + if arr_contents[i].found_group then + table.insert(filtered_arr_contents, arr_contents[i]) + end + end + end + + if filter:match('/:group%%(.*)%%') then + filter = filter:match('/:group%%(.*)%%') + for i = 1, #arr_contents do + if arr_contents[i].found_group and filter == get_group_properties(tonumber(arr_contents[i].found_group)).name then + table.insert(filtered_arr_contents, arr_contents[i]) + end + end + end + + if filter == 'keybinds' then + for i = 1, #arr_contents do + if arr_contents[i].found_slot then + table.insert(filtered_arr_contents, arr_contents[i]) + end + end + end + + if filter == 'recents' then + table.sort(arr_contents, function(a, b) return a['found_sequence'] < b['found_sequence'] end) + local unique_values = {} + local list_total = #arr_contents + + if filePath == arr_contents[#arr_contents].found_path and tonumber(arr_contents[#arr_contents].found_time) == 0 then + list_total = list_total -1 + end + + for i = list_total, 1, -1 do + if not has_value(unique_values, arr_contents[i].found_path) then + table.insert(unique_values, arr_contents[i].found_path) + table.insert(filtered_arr_contents, arr_contents[i]) + end + end + table.sort(filtered_arr_contents, function(a, b) return a['found_sequence'] < b['found_sequence'] end) + end + + if filter == 'distinct' then + table.sort(arr_contents, function(a, b) return a['found_sequence'] < b['found_sequence'] end) + local unique_values = {} + local list_total = #arr_contents + + if filePath == arr_contents[#arr_contents].found_path and tonumber(arr_contents[#arr_contents].found_time) == 0 then + list_total = list_total -1 + end + + for i = list_total, 1, -1 do + if arr_contents[i].found_directory and not has_value(unique_values, arr_contents[i].found_directory) and not starts_protocol(protocols, arr_contents[i].found_path) then + table.insert(unique_values, arr_contents[i].found_directory) + table.insert(filtered_arr_contents, arr_contents[i]) + end + end + table.sort(filtered_arr_contents, function(a, b) return a['found_sequence'] < b['found_sequence'] end) + end + + if filter == 'fileonly' then + for i = 1, #arr_contents do + if tonumber(arr_contents[i].found_time) == 0 then + table.insert(filtered_arr_contents, arr_contents[i]) + end + end + end + + if filter == 'timeonly' then + for i = 1, #arr_contents do + if tonumber(arr_contents[i].found_time) > 0 then + table.insert(filtered_arr_contents, arr_contents[i]) + end + end + end + + if filter == 'titleonly' then + for i = 1, #arr_contents do + if arr_contents[i].found_title then + table.insert(filtered_arr_contents, arr_contents[i]) + end + end + end + + if filter == 'protocols' then + for i = 1, #arr_contents do + if starts_protocol(o.logging_protocols, arr_contents[i].found_path) then + table.insert(filtered_arr_contents, arr_contents[i]) + end + end + end + + if filter == 'keywords' then + for i = 1, #arr_contents do + if contain_value(o.keywords_filter_list, arr_contents[i].found_line) then + table.insert(filtered_arr_contents, arr_contents[i]) + end + end + end + + if filter == 'playing' then + for i = 1, #arr_contents do + if arr_contents[i].found_path == filePath then + table.insert(filtered_arr_contents, arr_contents[i]) + end + end + end + + return filtered_arr_contents +end + +function get_osd_log_contents(filter, sort) + if not filter then filter = filterName end + if not sort then sort = get_list_sort(filter) end + + local current_sort + osd_log_contents = read_log_table() + if not osd_log_contents or not osd_log_contents[1] then return end + + current_sort = 'added-asc' + + if filter_log_contents(osd_log_contents, filter) then osd_log_contents = filter_log_contents(osd_log_contents, filter) end + if search_log_contents(osd_log_contents) then osd_log_contents = search_log_contents(osd_log_contents) end + + if sort ~= current_sort then + list_sort(osd_log_contents, sort) + end +end + +function get_list_sort(filter) + if not filter then filter = filterName end + + if filter == 'keybinds' then + available_sorts = {'added-asc', 'added-desc', 'keybind-asc', 'keybind-desc', 'time-asc', 'time-desc', 'alphanum-asc', 'alphanum-desc'} + else + available_sorts = {'added-asc', 'added-desc', 'time-asc', 'time-desc', 'alphanum-asc', 'alphanum-desc'} + 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 parse_8601(timestamp) + if not string.match(timestamp, '^(%d%d%d%d)-(%d%d)-(%d%d)T(%d%d):(%d%d):(%d%d)(.-)$') then return false end + local inYear, inMonth, inDay, inHour, inMinute, inSecond, inZone = string.match(timestamp, '^(%d%d%d%d)-(%d%d)-(%d%d)T(%d%d):(%d%d):(%d%d)(.-)$') + + local zHours, zMinutes = string.match(inZone, '^(.-):(%d%d)$') + + local returnTime = os.time({year=inYear, month=inMonth, day=inDay, hour=inHour, min=inMinute, sec=inSecond, isdst=false}) + + if zHours then + returnTime = returnTime - ((tonumber(zHours)*3600) + (tonumber(zMinutes)*60)) + end + + return returnTime + +end + +function draw_list(arr_contents) + local osd_msg = '' + 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 item_properties = {} --1.3# to hold all of the stuff that we extract from within this table, such as the osd_index, etc.. + + if o.header_text ~= '' then + osd_msg = osd_msg .. osd_header .. parse_header(o.header_text) .. osd_msg_end --1.3.0# made line break part of the config + end + + if search_active and not arr_contents[1] then --1.3.1# changed osd_log_contents to arr_contents + osd_msg = osd_msg .. 'No search results found' .. osd_msg_end + end + + local list_start + 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 #arr_contents <= o.list_show_amount then + list_start = 0 + showall = true + end + if list_start > math.max(#arr_contents - o.list_show_amount - 1, 0) then + list_start = #arr_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 == #arr_contents then break end + item_properties["item"] = arr_contents[#arr_contents - i] --1.3# stores the targetted item + item_properties['index'] = i --1.3# stores the index of which the item is found in the arr_contents table + + if o.quickselect_0to9_keybind and o.list_show_amount <= 10 then + key = key + 1 + if key == 10 then key = 0 end + item_properties['quickselect'] = key --1.3# added osd_key to item_properties to call it in parse_list_item + 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 + + if o.list_content_text ~= '' then --1.3# use parse_list_item to make the list customizable + osd_msg = osd_msg..osd_color..parse_list_item(o.list_content_text, item_properties) .. osd_msg_end --1.3.0# made line break part of the config + 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 osd_log_contents ~= nil and osd_log_contents[1] then return end + local msg_text + if filterName ~= 'all' then + msg_text = filterName .. " filter in Bookmark Empty" + else + msg_text = "Bookmark 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 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_osd_log_contents(filter, sort) + + if action ~= 'hide-osd' then + if not osd_log_contents or not osd_log_contents[1] then + list_empty_error_msg() + filterName = prev_filter + get_osd_log_contents(filterName) + return + end + end + if not osd_log_contents and not search_active or not osd_log_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(osd_log_contents) + if utils.shared_script_property_set then + utils.shared_script_property_set('simplebookmark-menu-open', 'yes') + end + mp.set_property('user-data/simplebookmark/menu-open', 'yes') + if o.toggle_idlescreen then mp.commandv('script-message', 'osc-idlescreen', 'no', 'no_osd') end + 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 osd_log_contents or not osd_log_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 <= #osd_log_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, osd_log_contents[#osd_log_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, osd_log_contents[#osd_log_contents+1+i - list_cursor]}) + end + end + end + table.insert(list_highlight_cursor, {list_cursor, osd_log_contents[#osd_log_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 > #osd_log_contents then + list_cursor = 1 + elseif list_cursor_temp < 1 then + list_cursor = #osd_log_contents + end + end + + draw_list(osd_log_contents) +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(#osd_log_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 #osd_log_contents < o.list_show_amount then + select(#osd_log_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 #osd_log_contents - list_cursor >= o.list_show_amount then + select(o.list_show_amount, action) + else + select(#osd_log_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_osd_log_contents(filterName) + if not osd_log_contents or not osd_log_contents[1] then return end + + if #list_highlight_cursor < #osd_log_contents then + for i=1, #osd_log_contents do + if not has_value(list_highlight_cursor, i, 1) then + table.insert(list_highlight_cursor, {i, osd_log_contents[#osd_log_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 osd_log_contents or not osd_log_contents[1] then return end + if not target_time then + if not osd_log_contents[#osd_log_contents - list_cursor + 1] then return end --1.3.0# fixes crash when loading an entry that doesn't exist + seekTime = tonumber(osd_log_contents[#osd_log_contents - list_cursor + 1].found_time) + o.resume_offset + if (seekTime < 0) then + seekTime = 0 + end + else + seekTime = target_time + end + if file_exists(osd_log_contents[#osd_log_contents - list_cursor + 1].found_path) or starts_protocol(protocols, osd_log_contents[#osd_log_contents - list_cursor + 1].found_path) then + local list_filename, list_filepath, list_filetitle = get_local_names(osd_log_contents[#osd_log_contents - list_cursor + 1]) --1.3# use the name that automatically falls back instead for osd printing or msg (solves the issue that causes concatinating found_name to crash because it sometimes doesn't exist due to parsing changes) + if not add_playlist then + if o.preserve_video_settings then mp.command("write-watch-later-config") end--1.3.1# option to preserve video settings by using write-watch-later-config when loading bookmark replaces current file #84 + if filePath ~= osd_log_contents[#osd_log_contents - list_cursor + 1].found_path then + mp.commandv('loadfile', osd_log_contents[#osd_log_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_filename.. ' 🕒 ' .. 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_filename .. ' | '.. format_time(seekTime)) + else + mp.commandv('loadfile', osd_log_contents[#osd_log_contents - list_cursor + 1].found_path, 'append-play') + if o.osd_messages == true then + mp.osd_message('Added into Playlist:\n'..list_filename..' ') + end + msg.info('Added the below file into playlist:\n' .. list_filepath) + end + else + if o.osd_messages == true then + mp.osd_message('File Doesn\'t Exist:\n' .. osd_log_contents[#osd_log_contents - list_cursor + 1].found_path) --1.3# cant use the list_filepath because file doesn't exist so it returns nil + end + msg.info('The file below doesn\'t seem to exist:\n' .. osd_log_contents[#osd_log_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 same_path_log_delete(target_path, entry_limit, arr_contents) + --1.2.5# seperate function for entry_limit + if not target_path then return msg.error('same_path_log_delete no target_path defined') end + if not arr_contents then --1.2.6# ability to pass array (usually automatically defining array here is fine, but just for performance sake when calling multiple functions that use the same array) + arr_contents = read_log_table() + if not arr_contents or not arr_contents[1] then return end + end + + --1.2.7# return deleted_entries for o.overwrite_preserve_properties option + local deleted_entries = {} + local trigger_delete = false + + if entry_limit and entry_limit > -1 then + local entries_found = 0 + for i = #arr_contents, 1, -1 do + if arr_contents[i].found_path == target_path and entries_found < entry_limit then + entries_found = entries_found + 1 + elseif arr_contents[i].found_path == target_path and entries_found >= entry_limit then + table.insert(deleted_entries, arr_contents[i]) --1.2.7# store entries that will be deleted in a new array + table.remove(arr_contents,i) + trigger_delete = true + end + end + end + + if not trigger_delete then return end + local f = io.open(log_fullpath, "w+") + if arr_contents ~= nil and arr_contents[1] then + for i = 1, #arr_contents do + f:write(("%s\n"):format(arr_contents[i].found_line)) + end + end + f:close() + return deleted_entries +end + + +function find_entry(round, target_path, target_time) --1.2.6# changed it to find_entry to have the sequence and any other additional property + --1.2.5# get the entry log sequence which is basically the id using path and time + if not target_path or not target_time then return msg.error('find_entry no target_path or target_time defined') end + local temp_log_contents = read_log_table() + if not temp_log_contents or not temp_log_contents[1] then return end + + for i = #temp_log_contents, 1, -1 do + if not round then + if temp_log_contents[i].found_path == target_path and tonumber(temp_log_contents[i].found_time) == target_time then + return temp_log_contents[i] + end + else + if temp_log_contents[i].found_path == target_path and math.floor(tonumber(temp_log_contents[i].found_time)) == target_time then + return temp_log_contents[i] + end + end + end +end + + +function delete_log_entry(target_sequence, arr_contents) + --1.2.5# new function to delete based on sequence which is (id) + if not target_sequence then return end --1.2.5# if no sequence found then just exit the function + if not arr_contents then --1.2.5# ability to pass an array instead of looping through + arr_contents = read_log_table() + if not arr_contents or not arr_contents[1] then return end + end + + table.remove(arr_contents, target_sequence) + + local f = io.open(log_fullpath, "w+") + if arr_contents ~= nil and arr_contents[1] then + for i = 1, #arr_contents do + f:write(("%s\n"):format(arr_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 + local temp_log_contents = read_log_table() + if not temp_log_contents or not temp_log_contents[1] then return end + + local log_contents_length = #temp_log_contents + + for i = 1, log_contents_length do + for j=1, #list_highlight_cursor do + if temp_log_contents[log_contents_length+1-i] then + if temp_log_contents[log_contents_length+1-i].found_sequence == list_highlight_cursor[j][2].found_sequence then + table.remove(temp_log_contents, log_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 temp_log_contents ~= nil and temp_log_contents[1] then + for i = 1, #temp_log_contents do + f:write(("%s\n"):format(temp_log_contents[i].found_line)) + end + end + f:close() + +end + +function delete_selected() + --1.2.5# replace with new delete_log_entry that uses sequence id, and used local variables with or statement just in case + local list_sequence = osd_log_contents[#osd_log_contents - list_cursor + 1].found_sequence + local list_filepath = osd_log_contents[#osd_log_contents - list_cursor + 1].found_path or "" + local list_seektime = tonumber(osd_log_contents[#osd_log_contents - list_cursor + 1].found_time) or 0 + + if not list_sequence then + msg.info("Failed to delete") + return + end + delete_log_entry(list_sequence) + msg.info("Deleted \"" .. list_filepath .. "\" | " .. format_time(list_seektime)) +end + +function list_delete(action) + if not action then + delete_selected() + elseif action == 'highlight' then + delete_log_entry_highlighted() + end + get_osd_log_contents() + if #osd_log_contents == 0 then + display_list('all') + select(0) + elseif list_cursor < #osd_log_contents + 1 then + select(0) + else + list_move_last() + end +end + +function get_total_duration(action) + if not osd_log_contents or not osd_log_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 = #osd_log_contents, 1, -1 do + if tonumber(osd_log_contents[i][action]) > 0 then + list_total_duration = list_total_duration + osd_log_contents[i][action] + end + end + end + return list_total_duration +end + +function list_cycle_sort() + if filterName == 'keybinds' then + available_sorts = {'added-asc', 'added-desc', 'keybind-asc', 'keybind-desc', 'time-asc', 'time-desc', 'alphanum-asc', 'alphanum-desc'} + else + available_sorts = {'added-asc', 'added-desc', 'time-asc', 'time-desc', 'alphanum-asc', 'alphanum-desc'} + end + + 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_osd_log_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, #osd_log_contents do + for j=1, #list_highlight_cursor do + if osd_log_contents[#osd_log_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 > #osd_log_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_osd_log_contents(o.filters_and_sequence[i + pos]) + if osd_log_contents ~= nil and osd_log_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_osd_log_contents(o.filters_and_sequence[i + pos]) + if osd_log_contents ~= nil and osd_log_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_osd_log_contents(o.filters_and_sequence[i]) + if osd_log_contents ~= nil and osd_log_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_osd_log_contents(o.filters_and_sequence[i]) + if osd_log_contents ~= nil and osd_log_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) + bind_keys(o.keybinds_remove_keybind, 'keybind-slot-remove', slot_remove) + bind_keys(o.keybinds_remove_highlighted_keybind, 'keybind-slot-remove-highlight', function()slot_remove('highlight')end) + bind_keys(o.list_group_add_cycle_keybind, 'group-add-cycle', list_group_add_cycle) + bind_keys(o.list_group_add_cycle_highlighted_keybind, 'group-add-cycle-highlight', function()list_group_add_cycle('highlight')end) + bind_keys(o.list_groups_remove_keybind, 'group-remove', group_remove) + bind_keys(o.list_groups_remove_highlighted_keybind, 'group-remove-highlight', function()group_remove('highlight')end) + + for i = 1, #o.groups_list_and_keybind do + if not o.groups_list_and_keybind[i][2] then break end + mp.add_forced_key_binding(o.groups_list_and_keybind[i][2], 'group-add-'..i, function()group_add(i)end) + end + for i = 1, #o.groups_list_and_keybind do + if not o.groups_list_and_keybind[i][3] then break end + mp.add_forced_key_binding(o.groups_list_and_keybind[i][3], 'group-add-highlight-'..i, function()group_add(i, 'highlight')end) + end + + 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') + unbind_keys(o.keybinds_remove_keybind, 'keybind-slot-remove') + unbind_keys(o.keybinds_remove_keybind, 'keybind-slot-remove-highlight') + + unbind_keys(o.list_group_add_cycle_keybind, 'group-add-cycle') + unbind_keys(o.list_group_add_cycle_highlighted_keybind, 'group-add-cycle-highlight') + unbind_keys(o.list_groups_remove_keybind, 'group-remove') + unbind_keys(o.list_groups_remove_highlighted_keybind, 'group-remove-highlight') + + for i = 1, #o.groups_list_and_keybind do + if not o.groups_list_and_keybind[i][2] then break end + mp.remove_key_binding('group-add-'..i) + end + for i = 1, #o.groups_list_and_keybind do + if not o.groups_list_and_keybind[i][3] then break end + mp.remove_key_binding('group-add-highlight-'..i) + end + + 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('simplebookmark-menu-open', 'no') + end + mp.set_property('user-data/simplebookmark/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_osd_log_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 osd_log_contents[1] then + search_active = 'not_typing' + elseif not osd_log_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 = #osd_log_contents + get_osd_log_contents(filterName) + + if prev_contents_length ~= #osd_log_contents then + list_highlight_cursor = {} + end + + if character ~= '' and #osd_log_contents > 0 or action ~= nil and #osd_log_contents > 0 then + select(1-list_cursor) + elseif #osd_log_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) + bind_keys(o.keybinds_remove_keybind, 'keybind-slot-remove', function() slot_remove() list_search_not_typing_mode(true) end) + bind_keys(o.keybinds_remove_keybind, 'keybind-slot-remove-highlight', function() slot_remove('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--------- + +--Modify Additional Log Parameters-- +function remove_all_additional_param_log_entry(index, log_text) + if not index or not log_text then return end + local temp_log_contents = read_log_table() + if not temp_log_contents or not temp_log_contents[1] then return end + + for i = #temp_log_contents, 1, -1 do + if temp_log_contents[i].found_line:find(log_text..index) then + temp_log_contents[i].found_line = string.gsub(temp_log_contents[i].found_line, ' | '..log_text..index, "") + end + end + + f = io.open(log_fullpath, "w+") + if temp_log_contents ~= nil and temp_log_contents[1] then + for i = 1, #temp_log_contents do + f:write(("%s\n"):format(temp_log_contents[i].found_line)) + end + end + f:close() +end + +function remove_additional_param_log_entry(index, target, log_text) + if not index or not target or not log_text then return msg.error('remove_additional_param_log_entry parameters not defined') end + if not osd_log_contents or not osd_log_contents[1] then return end + local temp_log_contents = read_log_table() + if not temp_log_contents or not temp_log_contents[1] then return end + + local log_index = osd_log_contents[target].found_sequence + + if temp_log_contents[log_index].found_line:find(log_text..index) then + temp_log_contents[log_index].found_line = string.gsub(temp_log_contents[log_index].found_line, ' | '..log_text..index, "") + else + return msg.error('temp_log_contents[log_index].found_line is not found') + end + + f = io.open(log_fullpath, "w+") + if temp_log_contents ~= nil and temp_log_contents[1] then + for i = 1, #temp_log_contents do + f:write(("%s\n"):format(temp_log_contents[i].found_line)) + end + end + f:close() +end + +function add_additional_param_log_entry(index, target, log_text) + if not index or not target or not log_text then return msg.error('add_additional_param_log_entry parameters not defined') end + if not osd_log_contents or not osd_log_contents[1] then return end + local temp_log_contents = read_log_table() + if not temp_log_contents or not temp_log_contents[1] then return end + local log_index = osd_log_contents[target].found_sequence + + if temp_log_contents[log_index].found_line then + if temp_log_contents[log_index].found_line:sub(-1) ~= ' ' then + temp_log_contents[log_index].found_line = temp_log_contents[log_index].found_line..' | '..log_text .. index..' | ' + else + temp_log_contents[log_index].found_line = temp_log_contents[log_index].found_line..log_text .. index..' | ' + end + else + return msg.error('temp_log_contents[log_index].found_line is not found') + end + + f = io.open(log_fullpath, "w+") + if temp_log_contents ~= nil and temp_log_contents[1] then + for i = 1, #temp_log_contents do + f:write(("%s\n"):format(temp_log_contents[i].found_line)) + end + end + f:close() +end +--End Of Modify Additional Log Parameters-- + +--Keybind Slot Feature-- +function list_slot_remove(index, action) + if not list_drawn then return end + if not osd_log_contents or not osd_log_contents[1] then return end + if not index then index = tonumber(osd_log_contents[#osd_log_contents - list_cursor + 1].found_slot) end + + if not index then + if action ~= 'silent' then msg.info("Failed to remove") end + return + end + remove_all_additional_param_log_entry(index, log_keybind_text) + if action ~= 'silent' then msg.info('Removed Keybind: ' .. get_slot_keybind(index)) end +end + +function list_slot_remove_highlighted() + if not list_drawn then return end + if not list_highlight_cursor or not list_highlight_cursor[1] then return end + if not osd_log_contents or not osd_log_contents[1] then return end + + local slotIndex + for i = 1, #osd_log_contents do + for j=1, #list_highlight_cursor do + if osd_log_contents[#osd_log_contents+1-i] then + if osd_log_contents[#osd_log_contents+1-i].found_sequence == list_highlight_cursor[j][2].found_sequence then + slotIndex = tonumber(osd_log_contents[#osd_log_contents+1-i].found_slot) + if slotIndex then + remove_all_additional_param_log_entry(slotIndex, log_keybind_text) + msg.info('Removed Keybind: ' .. get_slot_keybind(slotIndex)) + end + end + end + end + end +end + +function list_slot_add(index) + if not list_drawn then return end + if not osd_log_contents or not osd_log_contents[1] then return end + if not index then return end + + local cursor_filename, cursor_filepath, cursor_filetitle = get_local_names(osd_log_contents[#osd_log_contents - list_cursor + 1]) --1.2.4# added the new name calling method to fix issue of unable to add urls into groups or slots + local cursor_seektime = tonumber(osd_log_contents[#osd_log_contents - list_cursor + 1].found_time) + if not cursor_filename or not cursor_seektime then + msg.info("Failed to add slot") + return + end + + + local slotIndex = osd_log_contents[#osd_log_contents - list_cursor + 1].found_slot + if slotIndex then + remove_additional_param_log_entry(slotIndex,#osd_log_contents-list_cursor+1, log_keybind_text) + end + + list_slot_remove(index, 'silent') + add_additional_param_log_entry(index, #osd_log_contents-list_cursor+1, log_keybind_text) + msg.info('Added Keybind:\n' .. cursor_filetitle .. ' 🕒 ' .. format_time(cursor_seektime) .. ' ⌨ ' .. get_slot_keybind(index)) +end + +function slot_remove(action) + if not action then + list_slot_remove() + elseif action == 'highlight' then + list_slot_remove_highlighted() + end + get_osd_log_contents() + if #osd_log_contents == 0 then + display_list('all') + return + elseif list_cursor ~= #osd_log_contents + 1 then + select(0) + else + select(-1) + end +end + +function slot_add(index) + if not index then return end + + list_slot_add(index) + get_osd_log_contents() + if #osd_log_contents == 0 then + list_cursor = 0 + select(list_cursor) + elseif list_cursor ~= #osd_log_contents + 1 then + select(0) + else + select(-1) + end +end +--End of Keybind Slot Feature-- + +--Group Feature-- +function list_group_remove(action) + if not list_drawn then return end + if not osd_log_contents or not osd_log_contents[1] then return end + + local groupCursorIndex = tonumber(osd_log_contents[#osd_log_contents - list_cursor + 1].found_group) + if not groupCursorIndex then + if action ~= 'silent' then msg.info("Failed to remove") end + return + end + remove_additional_param_log_entry(groupCursorIndex, #osd_log_contents-list_cursor+1, log_group_text) + if action ~= 'silent' then msg.info('Removed Group: ' .. get_group_properties(groupCursorIndex).name) end +end + +function list_group_remove_highlighted() + if not list_drawn then return end + if not list_highlight_cursor or not list_highlight_cursor[1] then return end + if not osd_log_contents or not osd_log_contents[1] then return end + + local groupIndex + for i = 1, #osd_log_contents do + for j=1, #list_highlight_cursor do + if osd_log_contents[#osd_log_contents+1-i] then + if osd_log_contents[#osd_log_contents+1-i].found_sequence == list_highlight_cursor[j][2].found_sequence then + groupIndex = tonumber(osd_log_contents[#osd_log_contents+1-i].found_group) + if groupIndex then + remove_additional_param_log_entry(groupIndex, #osd_log_contents+1-i, log_group_text) + msg.info('Removed Group: ' .. get_group_properties(groupIndex).name) + end + end + end + end + end +end + +function list_group_add(index) + if not list_drawn then return end + if not osd_log_contents or not osd_log_contents[1] then return end + if not index then return end + + local cursor_filename, cursor_filepath, cursor_filetitle = get_local_names(osd_log_contents[#osd_log_contents - list_cursor + 1]) --1.2.4# added the new name calling method to fix issue of unable to add urls into groups or slots + local cursor_seektime = tonumber(osd_log_contents[#osd_log_contents - list_cursor + 1].found_time) + if not cursor_filename or not cursor_seektime then + msg.info("Failed to add group") + return + end + + list_group_remove('silent') + add_additional_param_log_entry(index, #osd_log_contents-list_cursor+1, log_group_text) + msg.info('Added Group:\n' .. cursor_filename .. ' 🕒 ' .. format_time(cursor_seektime) .. ' 🖿 ' .. get_group_properties(index).name) +end + +function list_group_add_highlighted(index) + if not list_drawn then return end + if not list_highlight_cursor or not list_highlight_cursor[1] then return end + if not osd_log_contents or not osd_log_contents[1] then return end + if not index then return end + list_group_remove_highlighted() + + for i = 1, #osd_log_contents do + for j=1, #list_highlight_cursor do + if osd_log_contents[#osd_log_contents+1-i] then + if osd_log_contents[#osd_log_contents+1-i].found_sequence == list_highlight_cursor[j][2].found_sequence then + add_additional_param_log_entry(index, #osd_log_contents+1-i, log_group_text) + msg.info('Added Group: ' .. get_group_properties(index).name) + end + end + end + end +end + +function list_group_add_cycle(action) + if not list_drawn then return end + if not osd_log_contents or not osd_log_contents[1] then return end + + local next_index = tonumber(osd_log_contents[#osd_log_contents - list_cursor + 1].found_group) + if next_index then next_index = next_index + 1 else next_index = 0 end + if next_index > #o.groups_list_and_keybind or next_index == 0 then + next_index = 1 + end + + if not action then + group_add(next_index) + elseif action == 'highlight' then + group_add(next_index, action) + end +end + +function group_remove(action) + if not action then + list_group_remove() + elseif action == 'highlight' then + list_group_remove_highlighted() + end + get_osd_log_contents() + if #osd_log_contents == 0 then + display_list('all') + return + elseif list_cursor ~= #osd_log_contents + 1 then + select(0) + else + select(-1) + end +end + +function group_add(index, action) + if not index then return end + + if not action then + list_group_add(index) + elseif action == 'highlight' then + list_group_add_highlighted(index) + end + get_osd_log_contents() + if #osd_log_contents == 0 then + list_cursor = 0 + select(list_cursor) + elseif list_cursor ~= #osd_log_contents + 1 then + select(0) + else + select(-1) + end +end +--End of Group Feature-- + +function mark_chapter() + if not o.mark_bookmark_as_chapter then return end + + local all_chapters = mp.get_property_native("chapter-list") + local chapter_index = 0 + local chapters_time = {} + + get_osd_log_contents() + if not osd_log_contents or not osd_log_contents[1] then return end + for i = 1, #osd_log_contents do + if osd_log_contents[i].found_path == filePath and tonumber(osd_log_contents[i].found_time) > 0 then + table.insert(chapters_time, tonumber(osd_log_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 = 'SimpleBookmark ' .. 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 + if o.preserve_video_settings then mp.command("write-watch-later-config") end--1.3.1# option to preserve video settings by using write-watch-later-config when saving bookmark #84 + + local prev_seekTime = seekTime + local deleted_entries = {} --1.2.7# add it above since we need to call it later for preserving properties + + seekTime = (mp.get_property_number('time-pos') or 0) + if target_time then + seekTime = target_time + end + if seekTime < 0 then seekTime = 0 end + + local found_entry = find_entry(true, filePath, math.floor(seekTime)) --1.2.5# finds entry_sequence using new function --1.2.6# updated to find_entry + --1.2.8# first delete_log_entry to correctly overwrite the data (having same_path_log_delete() function runs first will result in overwriting wrong entry) + if found_entry and found_entry['found_sequence'] ~= nil then --1.2.6# if the entry exists then proceed to delete it + delete_log_entry(found_entry['found_sequence']) --1.2.5# deletes log entry using new function that uses sequence to delete --1.2.8# removed calling the array earlier and automatically call inside function + end + deleted_entries = same_path_log_delete(filePath, entry_limit) --1.2.5# seperate function to delete any additional entries based on the same_entry_limit set by user --1.2.7# assign it to varible since function now returns status and an array of deleted_entries --1.2.8# removed calling the array earlier and automatically call inside function + + local f = io.open(log_fullpath, "a+")--1.3# dont allow customization to date_format so it can be saved in a standard in which I can parse for search results, etc.. + if o.file_title_logging == 'all' then + f:write(("[%s] \"%s\" | %s | %s | %s | "):format(os.date("%Y-%m-%dT%H:%M:%S"), 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)) or o.file_title_logging == 'local' and not (starts_protocol(o.logging_protocols, filePath)) then --1.3# added file_title_logging for local + f:write(("[%s] \"%s\" | %s | %s | %s | "):format(os.date("%Y-%m-%dT%H:%M:%S"), 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("%Y-%m-%dT%H:%M:%S"), filePath, log_length_text .. tostring(fileLength), log_time_text .. tostring(seekTime))) + else + f:write(("[%s] %s | %s | %s | "):format(os.date("%Y-%m-%dT%H:%M:%S"), filePath, log_length_text .. tostring(fileLength), log_time_text .. tostring(seekTime))) + end + + f:write('\n') + f:close() + + + --1.2.6# restore properties if o.overwrite_preserve_properties is enabled + if found_entry and o.overwrite_preserve_properties then + local temp_log_contents = read_log_table() --1.2.6# loop through table with the new additions + if not temp_log_contents or not temp_log_contents[1] then return end + --1.2.6# when a slot or group was found previously, then add it + if found_entry['found_slot'] then + remove_all_additional_param_log_entry(found_entry['found_slot'], log_keybind_text) --1.2.9# replaced list_slot_remove with remove_all_.. function to avoid possible errors since list_slot_remove has a check for list_drawn + add_additional_param_log_entry(found_entry['found_slot'], #temp_log_contents, log_keybind_text) + end + if found_entry['found_group'] then + add_additional_param_log_entry(found_entry['found_group'], #temp_log_contents, log_group_text) + end + end + + --1.2.7# if an exact match is not found, and there are multiple deleted entries because of same_path_log_delete then add the latest deleted property to the newly added entry + if not found_entry and deleted_entries ~= nil and deleted_entries[1] and o.overwrite_preserve_properties then + local temp_log_contents = read_log_table() --1.2.6# loop through table with the new additions + if not temp_log_contents or not temp_log_contents[1] then return end + --1.2.7# loop through all deleted entries and get the first found slot and group then append it to the latest entry and then break loop + local break_table = false + for i = 1, #deleted_entries do + if deleted_entries[i] then + if deleted_entries[i].found_slot then + remove_all_additional_param_log_entry(deleted_entries[i].found_slot, log_keybind_text) --1.2.9# replaced list_slot_remove with remove_all_.. function to avoid possible errors since list_slot_remove has a check for list_drawn + add_additional_param_log_entry(deleted_entries[i].found_slot, #temp_log_contents, log_keybind_text) + break_table = true --1.2.7# break the table after addition is added since no need to continue looking for more + end + if deleted_entries[i].found_group then + add_additional_param_log_entry(deleted_entries[i].found_group, #temp_log_contents, log_group_text) + break_table = true --1.2.7# break the table after addition is added since no need to continue looking for more + end + if break_table then --1.2.7# if it found a slot and group or just slot or just a group then break the table + break + end + end + end + end + + if not update_seekTime then + seekTime = prev_seekTime + end +end + +function add_load_slot(key_index) + if not key_index then return end + + local current_filePath = mp.get_property('path') + local list_filepath, list_filetitle, list_seektime + if list_drawn then + slot_add(key_index) + else + local slot_taken = false + get_osd_log_contents() + if osd_log_contents ~= nil and osd_log_contents[1] then + for i = 1, #osd_log_contents do + if tonumber(osd_log_contents[i].found_slot) == key_index then + list_filepath = osd_log_contents[i].found_path + list_filetitle = osd_log_contents[i].found_name + list_seektime = tonumber(osd_log_contents[i].found_time) + slot_taken = true + break + end + end + if slot_taken then + if file_exists(list_filepath) or starts_protocol(protocols, list_filepath) then + if list_filepath ~= current_filePath then + if o.preserve_video_settings then mp.command("write-watch-later-config") end--1.3.1# option to preserve video settings by using write-watch-later-config when loading bookmark replaces current file #84 + mp.commandv('loadfile', list_filepath) + if o.keybinds_auto_resume then + resume_selected = true + end + elseif list_filepath == current_filePath and o.keybinds_auto_resume then + mp.commandv('seek', list_seektime, 'absolute', 'exact') + list_close_and_trash_collection() + elseif list_filepath == current_filePath and not o.keybinds_auto_resume then + mp.commandv('seek', 0, 'absolute', 'exact') + list_close_and_trash_collection() + end + if o.keybinds_auto_resume then + if o.osd_messages == true then + mp.osd_message('Loaded slot:' .. ' ⌨ ' .. get_slot_keybind(key_index) .. '\n' .. list_filetitle .. ' 🕒 ' .. format_time(list_seektime, o.osd_time_format[3], o.osd_time_format[2], o.osd_time_format[1])) + end + msg.info('Loaded slot:' .. ' ⌨ ' .. get_slot_keybind(key_index) .. '\n' .. list_filetitle .. ' 🕒 ' .. format_time(list_seektime)) + else + if o.osd_messages == true then + mp.osd_message('Loaded slot:' .. ' ⌨ ' .. get_slot_keybind(key_index) .. '\n' .. list_filetitle) + end + msg.info('Loaded slot:' .. ' ⌨ ' .. get_slot_keybind(key_index) .. '\n' .. list_filetitle) + end + else + if o.osd_messages == true then + mp.osd_message('File Doesn\'t Exist:\n' .. list_filepath) + end + msg.info('The file below doesn\'t seem to exist:\n' .. list_filepath) + return + end + else + if o.keybinds_empty_auto_create then + if filePath ~= nil then + if o.keybinds_empty_fileonly then + write_log(0) --1.2.9# reflect removal of key_index in write_log function, fixes bug and cleaner code + get_osd_log_contents() --1.2.9# reflect removal of key_index in write_log function, fixes bug and cleaner code -- also used get_osd instead of local because add_additional_param uses osd_log inside its function perhaps this should be changed + local current_slot = tonumber(osd_log_contents[#osd_log_contents].found_slot) --1.2.9# gets the slot of the current item + remove_all_additional_param_log_entry(current_slot, log_keybind_text) --1.2.9# removes all the slots of the current item + remove_all_additional_param_log_entry(key_index, log_keybind_text) --1.2.9# removes all the slots that are going to be added based on passed index + add_additional_param_log_entry(key_index, #osd_log_contents, log_keybind_text) --1.2.9# adds the slot of the passed index + else + write_log(false) --1.2.9# reflect removal of key_index in write_log function, fixes bug and cleaner code + get_osd_log_contents() --1.2.9# reflect removal of key_index in write_log function, fixes bug and cleaner code + local current_slot = tonumber(osd_log_contents[#osd_log_contents].found_slot) --1.2.9# gets the slot of the current item + remove_all_additional_param_log_entry(current_slot, log_keybind_text) --1.2.9# removes all the slots of the current item + remove_all_additional_param_log_entry(key_index, log_keybind_text) --1.2.9# removes all the slots that are going to be added based on passed index + add_additional_param_log_entry(key_index, #osd_log_contents, log_keybind_text) --1.2.9# adds the slot of the passed index + end + if o.osd_messages == true then + mp.osd_message('Bookmarked & Added Keybind:\n' .. fileTitle .. ' 🕒 ' .. format_time(mp.get_property_number('time-pos'), o.osd_time_format[3], o.osd_time_format[2], o.osd_time_format[1]) .. ' ⌨ ' .. get_slot_keybind(key_index)) + end + msg.info('Bookmarked the below & added keybind:\n' .. fileTitle .. ' 🕒 ' .. format_time(mp.get_property_number('time-pos')) .. ' ⌨ ' .. get_slot_keybind(key_index)) + else + if o.osd_messages == true then + mp.osd_message('Failed to Bookmark & Auto Create Keybind\nNo Video Found') + end + msg.info("Failed to bookmark & auto create keybind, no video found") + end + else + if o.osd_messages == true then + mp.osd_message('No Bookmark Slot For' .. ' ⌨ ' .. get_slot_keybind(key_index) .. ' Yet') + end + msg.info('No bookmark slot has been assigned for' .. ' ⌨ ' .. get_slot_keybind(key_index) .. ' keybind yet') + end + end + else + if o.osd_messages == true then + mp.osd_message('No Bookmark Slot For' .. ' ⌨ ' .. get_slot_keybind(key_index) .. ' Yet') + end + msg.info('No bookmark slot has been assigned for' .. ' ⌨ ' .. get_slot_keybind(key_index) .. ' keybind yet') + end + end +end + +function quicksave_slot(key_index) + if not key_index then return end + + if list_drawn then + slot_add(key_index) + else + if filePath ~= nil then + if o.keybinds_quicksave_fileonly then + write_log(0) --1.2.9# reflect removal of key_index in write_log function, fixes bug and cleaner code -- also used get_osd instead of local because add_additional_param uses osd_log inside its function perhaps this should be changed + get_osd_log_contents() --1.2.9# reflect removal of key_index in write_log function, fixes bug and cleaner code + local current_slot = tonumber(osd_log_contents[#osd_log_contents].found_slot) --1.2.9# gets the slot of the current item + remove_all_additional_param_log_entry(current_slot, log_keybind_text) --1.2.9# removes all the slots of the current item + remove_all_additional_param_log_entry(key_index, log_keybind_text) --1.2.9# removes all the slots that are going to be added based on passed index + add_additional_param_log_entry(key_index, #osd_log_contents, log_keybind_text) --1.2.9# adds the slot of the passed index + + if o.osd_messages == true then + mp.osd_message('Bookmarked Fileonly & Added Keybind:\n' .. fileTitle .. ' ⌨ ' .. get_slot_keybind(key_index)) + end + msg.info('Bookmarked the below & added keybind:\n' .. fileTitle .. ' ⌨ ' .. get_slot_keybind(key_index)) + else + write_log(false, true) --1.2.9# reflect removal of key_index in write_log function, fixes bug and cleaner code + get_osd_log_contents() --1.2.9# reflect removal of key_index in write_log function, fixes bug and cleaner code + local current_slot = tonumber(osd_log_contents[#osd_log_contents].found_slot) --1.2.9# gets the slot of the current item + remove_all_additional_param_log_entry(current_slot, log_keybind_text) --1.2.9# removes all the slots of the current item + remove_all_additional_param_log_entry(key_index, log_keybind_text) --1.2.9# removes all the slots that are going to be added based on passed index + add_additional_param_log_entry(key_index, #osd_log_contents, log_keybind_text) --1.2.9# adds the slot of the passed index + + if o.osd_messages == true then + mp.osd_message('Bookmarked & Added Keybind:\n' .. fileTitle .. ' 🕒 ' .. format_time(seekTime, o.osd_time_format[3], o.osd_time_format[2], o.osd_time_format[1]) .. ' ⌨ ' .. get_slot_keybind(key_index)) + end + msg.info('Bookmarked the below & added keybind:\n' .. fileTitle .. ' 🕒 ' .. format_time(seekTime) .. ' ⌨ ' .. get_slot_keybind(key_index)) + end + else + if o.osd_messages == true then + mp.osd_message('Failed to Bookmark & Auto Create Keybind\nNo Video Found') + end + msg.info("Failed to bookmark & auto create keybind, no video found") + end + end +end + +function bookmark_save() + if filePath ~= nil then + write_log(false, true, o.same_entry_limit) --1.2.9# reflect removal of key_index in write_log function + if list_drawn then + get_osd_log_contents() + select(0) + end + if o.osd_messages == true then + mp.osd_message('Bookmarked:\n' .. fileTitle .. ' 🕒 ' .. format_time(seekTime, o.osd_time_format[3], o.osd_time_format[2], o.osd_time_format[1])) + end + msg.info('Added the below to bookmarks\n' .. fileTitle .. ' 🕒 ' .. format_time(seekTime)) + elseif filePath == nil and o.bookmark_loads_last_idle then + osd_log_contents = read_log_table() + load(1) + else + if o.osd_messages == true then + mp.osd_message('Failed to Bookmark\nNo Video Found') + end + msg.info("Failed to bookmark, no video found") + end +end + +function bookmark_fileonly_save() + if filePath ~= nil then + write_log(0, false, o.same_entry_limit) --1.2.9# reflect removal of key_index in write_log function + if list_drawn then + get_osd_log_contents() + select(0) + end + if o.osd_messages == true then + mp.osd_message('Bookmarked File Only:\n' .. fileTitle) + end + msg.info('Added the below to bookmarks\n' .. fileTitle) + elseif filePath == nil and o.bookmark_fileonly_loads_last_idle then + osd_log_contents = read_log_table() + load(1, false, 0) + else + if o.osd_messages == true then + mp.osd_message('Failed to Bookmark\nNo Video Found') + end + msg.info("Failed to bookmark, no video found") + 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 ~= nil) then + mp.commandv('seek', seekTime, 'absolute', 'exact') + resume_selected = false + end + mark_chapter() +end) + +mp.observe_property("idle-active", "bool", function(_, v) + if v then --1.3.0# if idle is triggered + filePath, fileTitle, fileLength = nil --1.3.0# set it back to nil if idle is triggered for better trash collection. issue #69 + end + + if v and o.auto_run_list_idle ~= 'none' then + display_list(o.auto_run_list_idle, nil, 'hide-osd') + end + + if v and type(o.load_item_on_startup) == "number" and not loadTriggered then --1.3.0# option to immediately load an entry based on number + if o.load_item_on_startup == 0 then return end --1.3.0# if the entry loaded is 0 then exit this, this is automatically handled in load also but it is better to exit here since there will be a loop below this + + osd_log_contents = read_log_table() --1.3.0# get the item list to use load function + if not osd_log_contents or not osd_log_contents[1] then return end + + if o.load_item_on_startup == -1 then o.load_item_on_startup = #osd_log_contents end --1.3.0# specify -1 as last entry + load(o.load_item_on_startup) + end +end) + +bind_keys(o.bookmark_save_keybind, 'bookmark-save', bookmark_save) +bind_keys(o.bookmark_fileonly_keybind, 'bookmark-fileonly', bookmark_fileonly_save) + +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 + +for i = 1, #o.keybinds_add_load_keybind do + mp.add_forced_key_binding(o.keybinds_add_load_keybind[i], 'keybind-slot-' .. i, function()add_load_slot(i) end) +end + +for i = 1, #o.keybinds_quicksave_keybind do + mp.add_forced_key_binding(o.keybinds_quicksave_keybind[i], 'keybind-slot-save-' .. i, function()quicksave_slot(i) end) +end |
