diff --git a/.config/mpv/mpv.conf b/.config/mpv/mpv.conf index e12a472..36c0ffa 100644 --- a/.config/mpv/mpv.conf +++ b/.config/mpv/mpv.conf @@ -71,10 +71,10 @@ lavfi-complex=vid.vd-lavc-threads=16 # Set cache to avoid lag -#cache=yes +cache=yes # Limit the amount of data demuxed (read from the file) ahead of playback -#demuxer-max-bytes=700M +demuxer-max-bytes=700M # shaders glsl-shader=~~/shaders/Anime4K_Deblur_Original.glsl @@ -104,9 +104,11 @@ cscale=ewa_lanczos #lavfi=[loudnorm=I=-22:TP=-1.5:LRA=2] # BGR + sub-font-size=35 sub-border-size=1.5 sub-color="#ffffff" sub-border-color="#000000" sub-align-y=24 sub-ass-force-style=FontName='Calex Code JP',FontSize=48,Outline=0.5,PrimaryColour=&Hffffff&,SecondaryColour=&H000000&,OutlineColour=&H000000&,BackColour=&H000000&,Alignment=2 + diff --git a/.config/mpv/script-opts/SimpleHistory.conf b/.config/mpv/script-opts/SimpleHistory.conf new file mode 100644 index 0000000..7e7f09f --- /dev/null +++ b/.config/mpv/script-opts/SimpleHistory.conf @@ -0,0 +1,299 @@ +######----Settings For SimpleHistory 1.1.5----###### +####------Script Settings-----#### + +#--Auto run the list when opening mpv and there is no video / file loaded. 'none' for disabled. Or choose between: all, recents, distinct, protocols, fileonly, titleonly, timeonly, keywords. +auto_run_list_idle=recents + +#--The behavior when mpv launches and nothing is loaded. 'none' for disabled. 'resume' to automatically resume your last played item. 'resume-notime' to resume your last played item but starts from the beginning. +startup_idle_behavior=resume + +#--hides OSC idle screen message when opening and closing menu (could cause unexpected behavior if multiple scripts are triggering osc-idlescreen off) +toggle_idlescreen=no + +#--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 +resume_offset=-0.65 + +#--yes is for displaying osd messages when actions occur. Change to no will disable all osd messages generated from this script +osd_messages=yes + +#--none: for disabled. notification: a message to resume the previous reached time will be triggered. force: to forcefully resume last playback based on threshold +resume_option=notification + +#--0 to always trigger the resume option when the same video has been played previously, a value such as 5 will only trigger the resume option if the last played time starts after 5% of the video and ends before completion by 5% +resume_option_threshold=0 + +#--yes is for marking the time as a chapter. no disables mark as chapter behavior. +mark_history_as_chapter=no + +#--yes so that blacklist becomes a whitelist, resulting in stuff such as paths / websites that are added to history_blacklist to be saved into history +invert_history_blacklist=no + +#--Paths / URLs / Websites / Files / Protocols / Extensions, that wont be added to history automatically, e.g.: ["c:\\users\\eisa01\\desktop", "c:\\users\\eisa01\\desktop\\*", "c:\\temp\\naruto-01.mp4", "youtube.com", "https://dailymotion.com/", "avi", "https://www.youtube.com/watch?v=e8YBesRKq_U", ".jpeg", "magnet:", "https://", "ftp"] +history_blacklist=[""] + +#--Keybind that will be used to immediately load and resume last item when no video is playing. If video is playing it will resume to the last found position +history_resume_keybind=["ctrl+r", "ctrl+R"] + +#--Keybind that will be used to immediately load the last item without resuming when no video is playing. If video is playing then it will add into playlist +history_load_last_keybind=["alt+r", "alt+R"] + +#--Keybind that will be used to open the list along with the specified filter. Available filters: "all", "recents", "distinct", "protocols", "fileonly", "titleonly", "timeonly", "keywords". +open_list_keybind=[ ["h", "all"], ["H", "all"], ["r", "recents"], ["R", "recents"] ] + +#--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 filters: "all", "recents", "distinct", "protocols", "fileonly", "titleonly", "timeonly", "keywords". +list_filter_jump_keybind=[ ["h", "all"], ["H", "all"], ["r", "recents"], ["R", "recents"], ["d", "distinct"], ["D", "distinct"], ["f", "fileonly"], ["F", "fileonly"] ] + +####------Incognito Settings-----#### + +#--yes to automatically start incognito mode when mpv launches, no disables this behavior +auto_run_incognito_mode=no + +#--yes so that the file that had incognito mode triggered on gets removed from history automatically, no keeps the file in history that incognito mode triggered on +delete_incognito_entry=yes + +#--"none" for disabled, "deleted-restore" so that the the file that was removed when entering incognito automtically gets restored, "always" so that exiting incognito_mode always immediately updates entry into history +restore_incognito_entry=always + +#--Triggers incognito mode. When enabled files played wont be added to history until this mode is disabled. +history_incognito_mode_keybind=["ctrl+H"] + +####------Logging Settings------#### + +#--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_path=/:dir%mpvconf% + +#--name+extension of the file that will be used to store the log data +log_file=mpvHistory.log + +#--Date format in the log (see lua date formatting), e.g.:"%d/%m/%y %X" or "%d/%b/%y %X" +date_format=%A/%B %d/%m/%Y %X + +#--Change between all, protocols, none. This option will store the media title in log file, it is useful for websites / protocols because title cannot be parsed from links alone +file_title_logging=protocols + +#--add below (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") +logging_protocols=["https?://", "magnet:", "rtmp:"] + +#--Prefers to use filename over filetitle. Select between local, protocols, all, and none. "local" prefer filenames for videos that are not protocols. "protocols" will prefer filenames for protocols only. "all" will prefer filename over filetitle for both protocols and not protocols videos. "none" will always use filetitle instead of filename +prefer_filename_over_title=local + +#--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. +same_entry_limit=2 + +####------List Settings-------#### + +#--yes is for going up on the first item loops towards the last item and vise-versa. no disables this behavior. +loop_through_list=no + +#--no is for more items to show, then u must reach the end. yes is for new items to show after reaching the middle of list. +list_middle_loader=yes + +#--Show file paths instead of media-title +show_paths=no + +#--Show the number of each item before displaying its name and values. +show_item_number=yes + +#--Change to yes or no. Slices long filenames per the amount specified below +slice_longfilenames=no + +#--Amount for slicing long filenames +slice_longfilenames_amount=55 + +#--Change maximum number to show items at once +list_show_amount=15 + +#--Keybind entries from 0 to 9 for quick selection when list is open (list_show_amount = 10 is maximum for this feature to work) +quickselect_0to9_keybind=yes + +#--Will exit the list when double tapping the main list, even if the list was accessed through a different filter. +main_list_keybind_twice_exits=yes + +#--To smartly set the search as not typing (when search box is open) without needing to press ctrl+enter. +search_not_typing_smartly=yes + +#--"specific" to find a match of either a date, title, path / url, time. "any" to find any typed search based on combination of date, title, path / url, and time. "any-notime" to find any typed search based on combination of date, title, and path / url, but without looking for time (this is to reduce unwanted results). +search_behavior=any + +####------Filter Settings-------#### +##--available filters: "all" to display all the items. Or "recents" to display recently added items to log without duplicate. Or "distinct" to show recent saved entries for files in different paths. Or "fileonly" to display files saved without time. Or "timeonly" to display files that have time only. Or "keywords" to display files with matching keywords specified in the configuration. Or "playing" to show list of current playing file. + +#--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. +filters_and_sequence=["all", "recents", "distinct", "protocols", "playing", "fileonly", "titleonly", "keywords"] + +#--Keybind that will be used to go to the next available filter based on the filters_and_sequence +next_filter_sequence_keybind=["RIGHT", "MBTN_FORWARD"] + +#--Keybind that will be used to go to the previous available filter based on the filters_and_sequence +previous_filter_sequence_keybind=["LEFT", "MBTN_BACK"] + +#--yes is for bypassing the last filter to go to first filter when navigating through filters using arrow keys, and vice-versa. no disables this behavior. +loop_through_filters=yes + +#--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"] +keywords_filter_list=[""] + +####------Sort Settings-------#### +##--available sort: added-asc is for the newest added item to show first. Or added-desc for the newest added to show last. Or alphanum-asc is for A to Z approach with filename and episode number lower first. Or alphanum-desc is for its Z to A approach. Or time-asc, time-desc to sort the list based on time. + +#--the default sorting method for all the different filters in the list. select between: added-asc, added-desc, time-asc, time-desc, alphanum-asc, alphanum-desc +list_default_sort=added-asc + +#--Default sort for specific filters, e.g.: [ ["all", "alphanum-asc"], ["playing", "added-desc"] ] +list_filters_sort=[ ] + +#--Keybind to cycle through the different available sorts when list is open +list_cycle_sort_keybind=["alt+s", "alt+S"] + +####------List Design Settings------#### + +#--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. +list_alignment=7 + +#--The time type for items on the list. Select between: duration, length, remaining. +text_time_type=duration + +#--Time seperator that will be used before the saved time +time_seperator= 🕒 + +#--The text that indicates there are more items above. \N is for new line. \h is for hard space. +list_sliced_prefix=...\h\N\N + +#--The text that indicates there are more items below +list_sliced_suffix=... + +#--yes enables pre text for showing quickselect keybinds before the list. no to disable +quickselect_0to9_pre_text=no + +#--Text color for list in BGR hexadecimal +text_color=ffffff + +#--Font size for the text of list +text_scale=50 + +#--Black border size for the text of list +text_border=0.7 + +#--Text color of current cursor position in BGR +text_cursor_color=ffbf7f + +#--Font size for text of current cursor position in list +text_cursor_scale=50 + +#--Black border size for text of current cursor position in list +text_cursor_border=0.7 + +#--Pre text for highlighted multi-select item +text_highlight_pre_text=✅ + +#--Search color when in typing mode +search_color_typing=ffffaa + +#--Search color when not in typing mode and it is active +search_color_not_typing=00bfff + +#--Header color in BGR hexadecimal +header_color=00bfff + +#--Header text size for the list +header_scale=55 + +#--Black border size for the Header of list +header_border=0.8 + +#--Text to be shown as header for the list +#--Available header variables: %cursor%, %total%, %highlight%, %filter%, %search%, %listduration%, %listlength%, %listremaining% +#--User defined text that only displays if a variable is triggered: %prefilter%, %afterfilter%, %prehighlight%, %afterhighlight% %presearch%, %aftersearch%, %prelistduration%, %afterlistduration%, %prelistlength%, %afterlistlength%, %prelistremaining%, %afterlistremaining% +#--Variables explanation: %cursor: displays the number of cursor position in list. %total: total amount of items in current list. %highlight%: total number of highlighted items. %filter: shows the filter name, %search: shows the typed search. Example of user defined text that only displays if a variable is triggered of user: %prefilter: user defined text before showing filter, %afterfilter: user defined text after showing filter. + +header_text=⌛ History [%cursor%/%total%]%prehighlight%%highlight%%afterhighlight%%prelistduration%%listduration%%afterlistduration%%prefilter%%filter%%afterfilter%%presort%%sort%%aftersort%%presearch%%search%%aftersearch% + +#--Sort method that is hidden from header when using %sort% variable +header_sort_hide_text=added-asc + +#--Text to be shown before or after triggered variable in the header +header_sort_pre_text= \{ +header_sort_after_text=} +header_filter_pre_text= [Filter: +header_filter_after_text=] +header_search_pre_text=\h\N\N[Search= +header_search_after_text=..] +header_highlight_pre_text=✅ +header_highlight_after_text= +header_list_duration_pre_text= 🕒 +header_list_duration_after_text= +header_list_length_pre_text= 🕒 +header_list_length_after_text= +header_list_remaining_pre_text= 🕒 +header_list_remaining_after_text= + +####-----Time Format Settings-----#### +##--in the first parameter, you can define from the available styles: default, hms, hms-full, timestamp, timestamp-concise "default" to show in HH:MM:SS.sss format. "hms" to show in 1h 2m 3.4s format. "hms-full" is the same as hms but keeps the hours and minutes persistent when they are 0. "timestamp" to show the total time as timestamp 123456.700 format. "timestamp-concise" shows the total time in 123456.7 format (shows and hides decimals depending on availability). +##--in the second parameter, you can define whether to show milliseconds, round them or truncate them. Available options: 'truncate' to remove the milliseconds and keep the seconds. 0 to remove the milliseconds and round the seconds. 1 or above is the amount of milliseconds to display. The default value is 3 milliseconds. +##--in the third parameter you can define the seperator between hour:minute:second. "default" style is automatically set to ":", "hms", "hms-full" are automatically set to " ". You can define your own. Some examples: ["default", 3, "-"],["hms-full", 5, "."],["hms", "truncate", ":"],["timestamp-concise"],["timestamp", 0],["timestamp", "truncate"],["timestamp", 5] + +osd_time_format=["default", "truncate"] +list_time_format=["default", "truncate"] +header_duration_time_format=["hms", "truncate", ":"] +header_length_time_format=["hms", "truncate", ":"] +header_remaining_time_format=["hms", "truncate", ":"] + +####------List Keybind Settings------#### +#--Add below (after a comma) any additional keybind you want to bind. Or change the letter inside the quotes to change the keybind +#--Example of changing and adding keybinds: --From ["b", "B"] To ["b"]. --From [""] to ["alt+b"]. --From [""] to ["a" "ctrl+a", "alt+a"] + +#--Keybind that will be used to navigate up on the list +list_move_up_keybind=["UP", "WHEEL_UP"] + +#--Keybind that will be used to navigate down on the list +list_move_down_keybind=["DOWN", "WHEEL_DOWN"] + +#--Keybind that will be used to go to the first item for the page shown on the list +list_page_up_keybind=["PGUP"] + +#--Keybind that will be used to go to the last item for the page shown on the list +list_page_down_keybind=["PGDWN"] + +#--Keybind that will be used to navigate to the first item on the list +list_move_first_keybind=["HOME"] + +#--Keybind that will be used to navigate to the last item on the list +list_move_last_keybind=["END"] + +#--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_move_keybind=["SHIFT"] + +#--Keybind that will be used to highlight all displayed items on the list +list_highlight_all_keybind=["ctrl+a", "ctrl+A"] + +#--Keybind that will be used to remove all currently highlighted items from the list +list_unhighlight_all_keybind=["ctrl+d", "ctrl+D"] + +#--Keybind that will be used to load entry based on cursor position +list_select_keybind=["ENTER", "MBTN_MID"] + +#--Keybind that will be used to add entry to playlist based on cursor position +list_add_playlist_keybind=["CTRL+ENTER"] + +#--Keybind that will be used to add all highlighted entries to playlist +list_add_playlist_highlighted_keybind=["SHIFT+ENTER"] + +#--Keybind that will be used to close the list (closes search first if it is open) +list_close_keybind=["ESC", "MBTN_RIGHT"] + +#--Keybind that will be used to delete the entry based on cursor position +list_delete_keybind=["DEL"] + +#--Keybind that will be used to delete all highlighted entries from the list +list_delete_highlighted_keybind=["SHIFT+DEL"] + +#--Keybind that will be used to trigger search +list_search_activate_keybind=["ctrl+f", "ctrl+F"] + +#--Keybind that will be used to exit typing mode of search while keeping search open +list_search_not_typing_mode_keybind=["ALT+ENTER"] + +#--Keybind thats are ignored when list is open +list_ignored_keybind=["B", "b", "k", "K", "c", "C"] + +######----End of Settings----###### diff --git a/.config/mpv/script-opts/subs2srs.conf b/.config/mpv/script-opts/subs2srs.conf index 01376ad..117bd7b 100644 --- a/.config/mpv/script-opts/subs2srs.conf +++ b/.config/mpv/script-opts/subs2srs.conf @@ -190,7 +190,7 @@ audio_format=opus #audio_format=mp3 # Sane values are 16k-32k for opus, 64k-128k for mp3. -audio_bitrate=24k +audio_bitrate=20k # Set a pad to the dialog timings. 0.5 = half a second. # Pads are never applied to manually set timings. @@ -239,8 +239,8 @@ miscinfo_format=%n EP%d (%t) # Language of secondary subs. # If you leave this parameter empty, no secondary subs will be automatically loaded. -secondary_sub_lang=eng,en,rus,ru,bel,be -#secondary_sub_lang= +#secondary_sub_lang=eng,en,rus,ru,bel,be +secondary_sub_lang= # Hover area. # Proportion of the top part of the mpv window where the secondary subtitles are visible when hovered over. diff --git a/.config/mpv/script-opts/subs2srs.conf~ b/.config/mpv/script-opts/subs2srs.conf~ deleted file mode 100644 index 8c85444..0000000 --- a/.config/mpv/script-opts/subs2srs.conf~ +++ /dev/null @@ -1,271 +0,0 @@ -### -### Main mpvacious configuration file. -### Save this file to ~/.config/mpv/script-opts/subs2srs.conf -### - -## -## General settings -## - -# Anki deck for new cards. Subdecks are supported. -deck_name=Mining - -# Model names are listed in `Tools -> Manage note types` menu in Anki. -# If you don't have a model for Japanese, get it from -# https://tatsumoto.neocities.org/blog/setting-up-anki.html#import-an-example-mining-deck -model_name=Japanese sentences - -# Field names as they appear in the selected note type. -# If you set `audio_field` or `image_field` empty, -# the corresponding media file will not be created. -sentence_field=SentKanji -secondary_field=SentEng -audio_field=SentAudio -image_field=Image - -# The tag(s) added to new notes. Spaces separate multiple tags. -# Leave nothing after `=` to disable tagging completely. -# The following substitutions are supported: -# %n - the name of the video -# %t - timestamp -# %d - episode number (if none, returns nothing) -# %e - SUBS2SRS_TAGS environment variable (if you have it set) -note_tag=subs2srs -#note_tag=%n %t %e -#note_tag= - -# Size and name of the font used in the menu -menu_font_size=24 -menu_font_name=Noto Serif CJK JP - -## -## Toggleables. -## Possible values: `yes` or `no`. -## - -# Use FFmpeg encoder instead of mpv encoder -# If mpvacious encounters problems creating audio and images for Anki cards, -# setting this to `yes` should fix them. -# -# You need to install ffmpeg and add it to the PATH first. -# https://wiki.archlinux.org/title/FFmpeg -# https://www.ffmpeg.org/download.html -# -# FFmpeg encoder is unable to create audio and images from remote content (like YouTube videos). -use_ffmpeg=no - -# Automatically create the deck for new cards (see deck_name option) -create_deck=yes - -# Allow making notes with the same sentence field. -allow_duplicates=no - -# When mpv starts, automatically copy subs to the clipboard as they appear on screen. -# This option can be also toggled in the addon's OSD menu. -autoclip=yes - -# Command to run when autoclip is triggered. -# If empty, just copies text to the clipboard. -# If set, calls the external program. -# E.g., even though GoldenDict can watch the system clipboard, -# if you send subtitles directly to GoldenDict, you don't pollute the clipboard as much. -autoclip_command= -#autoclip_command=goldendict - -# Remove all spaces from the primary subtitle text. -# Set this to "yes" for languages without spaces like Japanese. -# However, if mpvacious detects any latin characters in the string, spaces will not be removed. -nuke_spaces=yes - -# if set to `yes`, the volume of the outputted audio file -# depends on the volume of the player at the time of export -tie_volumes=no - -# Remove text in parentheses and leading/trailing spaces or -# newlines that may interfere with Yomichan before copying -# subtitles to the clipboard -clipboard_trim_enabled=yes - -# Add media to fields before or after existing data -append_media=yes - -# Remove text in brackets before substituting %n into tag -tag_nuke_brackets=yes - -# Remove text in brackets before substituting %n into tag -tag_nuke_parentheses=no - -# Remove the episode number before substituting %n into tag -tag_del_episode_num=yes - -# Remove everything after the episode number before substituting %n into tag -# Does nothing if the previous option tag_del_episode_num is disabled. -tag_del_after_episode_num=yes - -# Convert filename to lowercase for tagging. -tag_filename_lowercase=no - -# Lets you disable anki browser manipulation by mpvacious. -disable_gui_browse=no - -# Play audio clip automatically in background -# after note creation (or note update) to ensure that the audio is correctly cut. -preview_audio=no - -# When selecting subtitle lines, print them on the screen. -show_selected_text=yes - -# For convenience, read config file from disk before a card is made. -# Useful if you change your config often since you won't have to restart mpv every time, -# but reading from disk takes some time. -reload_config_before_card_creation=yes - -## -## Image settings -## - -# Snapshot format. -# Do not switch to `jpg` unless your computer doesn't support `webp`. -snapshot_format=webp -#snapshot_format=jpg - -# Quality of produced image files. 0 = lowest, 100=highest. -snapshot_quality=15 - -# Image dimensions -# If either (but not both) of the width or height parameters is -2, -# the value will be calculated preserving the aspect-ratio. -snapshot_width=-2 -snapshot_height=200 - -# Screenshot (yes, no) -# Usually not required. -# When making Anki cards, create a screenshot (by calling 'screenshot-to-file') instead of a snapshot. -# If set to yes, image dimensions and quality cannot be controlled due to mpv limitations. -# 'snapshot_format' is still respected. -# When using this, a custom sync server is recommended, e.g. https://github.com/ankicommunity/anki-sync-server -screenshot=no - -# The exact image template used when exporting to Anki's image field. -# Adding data-editor-shrink="true" makes the image smaller by default within the Anki viewer -# on versions 2.1.53+ (equivalent of double-clicking on the image). -# You likely would not want to change this unless you know what you are doing. -image_template=snapshot -#image_template=snapshot - -# Similar to image_template but with audio. -# Normally, the user doesn't need to change this setting, -# but it may be needed for audio files to be playable on AnkiWeb. -audio_template=[sound:%s] -#audio_template= - -## -## Animated snapshots -## Animated snapshots will capture the video from the start to the end times selected when using mpvacious. -## - -# If enabled, generates animated snapshots (something like GIFs) instead of static snapshots. -animated_snapshot_enabled=no - -# Number of frame per seconds, a value between 0 and 30 (30 included) -# Higher values will increase both quality and file size, lower values will do the opposite -animated_snapshot_fps=10 - -# Animated snapshot dimensions -# If either (but not both) of the width or height parameters is -2, -# the value will be calculated preserving the aspect-ratio. -animated_snapshot_width=-2 -animated_snapshot_height=200 - -# Quality of the produced animation, 0 = lowest, 100 = highest -animated_snapshot_quality=5 - -## -## Audio settings -## - -# Audio format. -# Do not switch to `mp3` unless your computer doesn't support `opus`. -audio_format=opus -#audio_format=mp3 - -# Sane values are 16k-32k for opus, 64k-128k for mp3. -audio_bitrate=24k - -# Set a pad to the dialog timings. 0.5 = half a second. -# Pads are never applied to manually set timings. -audio_padding=0.0 -#audio_padding=0.5 - -## -## Forvo support (Yomichan users only) -## - -# yes - fetch audio from Forvo if Yomichan couldn't find the audio (default) -# always - always fetch audio from Forvo and replace the audio added by Yomichan -# no - never use Forvo -use_forvo=yes - -# Vocab field should be equal to {expression} field in Yomichan -vocab_field=VocabKanji - -# Vocab Audio field should be equal to {audio} field in Yomichan -vocab_audio_field=VocabAudio - -## -## Misc info -## Various context information that can be written on your cards in a specified field. -## - -# yes to enable or no to disable. -miscinfo_enable=yes - -# Field name -miscinfo_field=Notes - -# Format string used to fill the misc info field. -# It supports the same substitutions as `note_tag`. HTML is supported. -miscinfo_format=%n EP%d (%t) -#miscinfo_format=From mpvacious %n at %t. - -## -## Secondary subtitles -## Mpvacious can try automatically loading secondary subtitles that will appear at the top. -## For example, you may want to load English subs alongside Japanese subs. -## -## Secondary subtitles should be present in the container. -## But if you manually set secondary sid from the command line, mpvacious won't change it. -## - -# Language of secondary subs. -# If you leave this parameter empty, no secondary subs will be automatically loaded. -secondary_sub_lang=eng,en,rus,ru,bel,be -#secondary_sub_lang= - -# Hover area. -# Proportion of the top part of the mpv window where the secondary subtitles are visible when hovered over. -# Possible values: from 0.0 to 1.0 -secondary_sub_area=0.15 - -# Visibility state -# Can be set to: 'auto', 'never', 'always'. -# If set to 'never' or 'always', secondary_sub_area has no effect. -# If set to 'auto', visibility behaves according to the value of secondary_sub_area. -# Default binding to cycle this value: Ctrl+v. -secondary_sub_visibility=auto - -## -## Custom audio encoding arguments -## These arguments are added to the command line. -## `mpv` and `ffmpeg` accept slightly different parameters. -## Feel free to experiment for yourself, but be careful or media creation might stop working. -## - -# Ffmpeg -ffmpeg_audio_args=-af loudnorm=I=-16:TP=-1.5:LRA=11 -#ffmpeg_audio_args=-af silenceremove=1:0:-50dB - -# mpv -# mpv accepts each filter as a separate argument, e.g. --af-append=1 --af-append=2 -mpv_audio_args=--af-append=loudnorm=I=-16:TP=-1.5:LRA=11 -#mpv_audio_args=--af-append=silenceremove=1:0:-50dB diff --git a/.config/mpv/scripts/SimpleHistory.lua b/.config/mpv/scripts/SimpleHistory.lua new file mode 100644 index 0000000..b17d5a3 --- /dev/null +++ b/.config/mpv/scripts/SimpleHistory.lua @@ -0,0 +1,2275 @@ +-- Copyright (c) 2022, Eisa AlAwadhi +-- License: BSD 2-Clause License +-- Creator: Eisa AlAwadhi +-- Project: SimpleHistory +-- Version: 1.1.6 + +local o = { +---------------------------USER CUSTOMIZATION SETTINGS--------------------------- +--These settings are for users to manually change some options. +--Changes are recommended to be made in the script-opts directory. + + -----Script Settings---- + auto_run_list_idle = 'recents', --Auto run the list when opening mpv and there is no video / file loaded. 'none' for disabled. Or choose between: 'all', 'recents', 'distinct', 'protocols', 'fileonly', 'titleonly', 'timeonly', 'keywords'. + startup_idle_behavior = 'none', --The behavior when mpv launches and nothing is loaded. 'none' for disabled. 'resume' to automatically resume your last played item. 'resume-notime' to resume your last played item but starts from the beginning. + toggle_idlescreen = false, --hides OSC idle screen message when opening and closing menu (could cause unexpected behavior if multiple scripts are triggering osc-idlescreen off) + resume_offset = -0.65, --change to 0 so item resumes from the exact position, or decrease the value so that it gives you a little preview before loading the resume point + osd_messages = true, --true is for displaying osd messages when actions occur. Change to false will disable all osd messages generated from this script + resume_option = 'notification', --'none': for disabled. 'notification': a message to resume the previous reached time will be triggered. 'force': to forcefully resume last playback based on threshold + resume_option_threshold = 2, --0 to always trigger the resume option when the same video has been played previously, a value such as 5 will only trigger the resume option if the last played time starts after 5% of the video and ends before completion by 5% + mark_history_as_chapter = false, --true is for marking the time as a chapter. false disables mark as chapter behavior. + invert_history_blacklist = false, --true so that blacklist becomes a whitelist, resulting in stuff such as paths / websites that are added to history_blacklist to be saved into history + history_blacklist=[[ + [""] + ]], --Paths / URLs / Websites / Files / Protocols / Extensions, that wont be added to history automatically, e.g.: ["c:\\users\\eisa01\\desktop", "c:\\users\\eisa01\\desktop\\*", "c:\\temp\\naruto-01.mp4", "youtube.com", "https://dailymotion.com/", "avi", "https://www.youtube.com/watch?v=e8YBesRKq_U", ".jpeg", "magnet:", "https://", "ftp"] + history_resume_keybind=[[ + ["ctrl+r", "ctrl+R"] + ]], --Keybind that will be used to immediately load and resume last item when no video is playing. If video is playing it will resume to the last found position + history_load_last_keybind=[[ + ["alt+r", "alt+R"] + ]], --Keybind that will be used to immediately load the last item without resuming when no video is playing. If video is playing then it will add into playlist + open_list_keybind=[[ + [ ["h", "all"], ["H", "all"], ["r", "recents"], ["R", "recents"] ] + ]], --Keybind that will be used to open the list along with the specified filter. Available filters: 'all', 'recents', 'distinct', 'protocols', 'fileonly', 'titleonly', 'timeonly', 'keywords'. + list_filter_jump_keybind=[[ + [ ["h", "all"], ["H", "all"], ["r", "recents"], ["R", "recents"], ["d", "distinct"], ["D", "distinct"], ["f", "fileonly"], ["F", "fileonly"] ] + ]], --Keybind that is used while the list is open to jump to the specific filter (it also enables pressing a filter keybind twice to close list). Available fitlers: 'all', 'recents', 'distinct', 'protocols', 'fileonly', 'titleonly', 'timeonly', 'keywords'. + + -----Incognito Settings---- + auto_run_incognito_mode = false, --true to automatically start incognito mode when mpv launches, false disables this behavior + delete_incognito_entry = true, --true so that the file that had incognito mode triggered on gets removed from history automatically, false keeps the file in history that incognito mode triggered on + restore_incognito_entry = 'always', --'none' for disabled, 'deleted-restore' so that the the file that was removed when entering incognito automtically gets restored, 'always' so that exiting incognito_mode always immediately updates entry into history + history_incognito_mode_keybind=[[ + ["ctrl+H"] + ]], --Triggers incognito mode. When enabled files played wont be added to history until this mode is disabled. + + -----Logging Settings----- + log_path = '/:dir%mpvconf%', --Change to '/:dir%script%' for placing it in the same directory of script, OR change to '/:dir%mpvconf%' for mpv portable_config directory. OR write any variable using '/:var' then the variable '/:var%APPDATA%' you can use path also, such as: '/:var%APPDATA%\\mpv' OR '/:var%HOME%/mpv' OR specify the absolute path , e.g.: 'C:\\Users\\Eisa01\\Desktop\\' + log_file = 'mpvHistory.log', --name+extension of the file that will be used to store the log data + date_format = '%A/%B %d/%m/%Y %X', --Date format in the log (see lua date formatting), e.g.:'%d/%m/%y %X' or '%d/%b/%y %X' + file_title_logging = 'protocols', --Change between 'all', 'protocols', 'none'. This option will store the media title in log file, it is useful for websites / protocols because title cannot be parsed from links alone + logging_protocols=[[ + ["https?://", "magnet:", "rtmp:"] + ]], --add above (after a comma) any protocol you want its title to be stored in the log file. This is valid only for (file_title_logging = 'protocols' or file_title_logging = 'all') + prefer_filename_over_title = 'local', --Prefers to log filename over filetitle. Select between 'local', 'protocols', 'all', and 'none'. 'local' prefer filenames for videos that are not protocols. 'protocols' will prefer filenames for protocols only. 'all' will prefer filename over filetitle for both protocols and not protocols videos. 'none' will always use filetitle instead of filename + same_entry_limit = 2, --Limit saving entries with same path: -1 for unlimited, 0 will always update entries of same path, e.g. value of 3 will have the limit of 3 then it will start updating old values on the 4th entry. + + -----List Settings----- + loop_through_list = false, --true is for going up on the first item loops towards the last item and vise-versa. false disables this behavior. + list_middle_loader = true, --false is for more items to show, then u must reach the end. true is for new items to show after reaching the middle of list. + show_paths = false, --Show file paths instead of media-title + show_item_number = true, --Show the number of each item before displaying its name and values. + slice_longfilenames = false, --Change to true or false. Slices long filenames per the amount specified below + slice_longfilenames_amount = 55, --Amount for slicing long filenames + list_show_amount = 10, --Change maximum number to show items at once + quickselect_0to9_keybind = true, --Keybind entries from 0 to 9 for quick selection when list is open (list_show_amount = 10 is maximum for this feature to work) + main_list_keybind_twice_exits = true, --Will exit the list when double tapping the main list, even if the list was accessed through a different filter. + search_not_typing_smartly = true, --To smartly set the search as not typing (when search box is open) without needing to press ctrl+enter. + search_behavior = 'any', --'specific' to find a match of either a date, title, path / url, time. 'any' to find any typed search based on combination of date, title, path / url, and time. 'any-notime' to find any typed search based on combination of date, title, and path / url, but without looking for time (this is to reduce unwanted results). + + -----Filter Settings------ + --available filters: "all" to display all the items. Or "recents" to display recently added items to log without duplicate. Or "distinct" to show recent saved entries for files in different paths. Or "fileonly" to display files saved without time. Or "timeonly" to display files that have time only. Or "keywords" to display files with matching keywords specified in the configuration. Or "playing" to show list of current playing file. + filters_and_sequence=[[ + ["all", "recents", "distinct", "protocols", "playing", "fileonly", "titleonly", "keywords"] + ]], --Jump to the following filters and in the shown sequence when navigating via left and right keys. You can change the sequence and delete filters that are not needed. + next_filter_sequence_keybind=[[ + ["RIGHT", "MBTN_FORWARD"] + ]], --Keybind that will be used to go to the next available filter based on the filters_and_sequence + previous_filter_sequence_keybind=[[ + ["LEFT", "MBTN_BACK"] + ]], --Keybind that will be used to go to the previous available filter based on the filters_and_sequence + loop_through_filters = true, --true is for bypassing the last filter to go to first filter when navigating through filters using arrow keys, and vice-versa. false disables this behavior. + keywords_filter_list=[[ + [""] + ]], --Create a filter out of your desired 'keywords', e.g.: youtube.com will filter out the videos from youtube. You can also insert a portion of filename or title, or extension or a full path / portion of a path. e.g.: ["youtube.com", "mp4", "naruto", "c:\\users\\eisa01\\desktop"] + + -----Sort Settings------ + --available sort: 'added-asc' is for the newest added item to show first. Or 'added-desc' for the newest added to show last. Or 'alphanum-asc' is for A to Z approach with filename and episode number lower first. Or 'alphanum-desc' is for its Z to A approach. Or 'time-asc', 'time-desc' to sort the list based on time. + list_default_sort = 'added-asc', --the default sorting method for all the different filters in the list. select between 'added-asc', 'added-desc', 'time-asc', 'time-desc', 'alphanum-asc', 'alphanum-desc' + list_filters_sort=[[ + [ ] + ]], --Default sort for specific filters, e.g.: [ ["all", "alphanum-asc"], ["playing", "added-desc"] ] + list_cycle_sort_keybind=[[ + ["alt+s", "alt+S"] + ]], --Keybind to cycle through the different available sorts when list is open + + -----List Design Settings----- + list_alignment = 7, --The alignment for the list, uses numpad positions choose from 1-9 or 0 to disable. e,g.:7 top left alignment, 8 top middle alignment, 9 top right alignment. + text_time_type = 'duration', --The time type for items on the list. Select between 'duration', 'length', 'remaining'. + time_seperator = ' 🕒 ', --Time seperator that will be used before the time + list_sliced_prefix = '...\\h\\N\\N', --The text that indicates there are more items above. \\N is for new line. \\h is for hard space. + list_sliced_suffix = '...', --The text that indicates there are more items below. + quickselect_0to9_pre_text = false, --true enables pre text for showing quickselect keybinds before the list. false to disable + text_color = 'ffffff', --Text color for list in BGR hexadecimal + text_scale = 50, --Font size for the text of list + text_border = 0.7, --Black border size for the text of list + text_cursor_color = 'ffbf7f', --Text color of current cursor position in BGR hexadecimal + text_cursor_scale = 50, --Font size for text of current cursor position in list + text_cursor_border = 0.7, --Black border size for text of current cursor position in list + text_highlight_pre_text = '✅ ', --Pre text for highlighted multi-select item + search_color_typing = 'ffffaa', --Search color when in typing mode + search_color_not_typing = '00bfff', --Search color when not in typing mode and it is active + header_color = '00bfff', --Header color in BGR hexadecimal + header_scale = 55, --Header text size for the list + header_border = 0.8, --Black border size for the Header of list + header_text = '⌛ History [%cursor%/%total%]%prehighlight%%highlight%%afterhighlight%%prelistduration%%listduration%%afterlistduration%%prefilter%%filter%%afterfilter%%presort%%sort%%aftersort%%presearch%%search%%aftersearch%', --Text to be shown as header for the list + --Available header variables: %cursor%, %total%, %highlight%, %filter%, %search%, %listduration%, %listlength%, %listremaining% + --User defined text that only displays if a variable is triggered: %prefilter%, %afterfilter%, %prehighlight%, %afterhighlight% %presearch%, %aftersearch%, %prelistduration%, %afterlistduration%, %prelistlength%, %afterlistlength%, %prelistremaining%, %afterlistremaining% + --Variables explanation: %cursor: displays the number of cursor position in list. %total: total amount of items in current list. %highlight%: total number of highlighted items. %filter: shows the filter name, %search: shows the typed search. Example of user defined text that only displays if a variable is triggered of user: %prefilter: user defined text before showing filter, %afterfilter: user defined text after showing filter. + header_sort_hide_text = 'added-asc',--Sort method that is hidden from header when using %sort% variable + header_sort_pre_text = ' \\{',--Text to be shown before sort in the header, when using %presort% + header_sort_after_text = '}',--Text to be shown after sort in the header, when using %aftersort% + header_filter_pre_text = ' [Filter: ', --Text to be shown before filter in the header, when using %prefilter% + header_filter_after_text = ']', --Text to be shown after filter in the header, when using %afterfilter% + header_search_pre_text = '\\h\\N\\N[Search=', --Text to be shown before search in the header, when using %presearch% + header_search_after_text = '..]', --Text to be shown after search in the header, when using %aftersearch% + header_highlight_pre_text = '✅', --Text to be shown before total highlighted items of displayed list in the header + header_highlight_after_text = '', --Text to be shown after total highlighted items of displayed list in the header + header_list_duration_pre_text = ' 🕒 ', --Text to be shown before playback total duration of displayed list in the header + header_list_duration_after_text = '', --Text to be shown after playback total duration of displayed list in the header + header_list_length_pre_text = ' 🕒 ', --Text to be shown before playback total duration of displayed list in the header + header_list_length_after_text = '', --Text to be shown after playback total duration of displayed list in the header + header_list_remaining_pre_text = ' 🕒 ', --Text to be shown before playback total duration of displayed list in the header + header_list_remaining_after_text = '', --Text to be shown after playback total duration of displayed list in the header + + -----Time Format Settings----- + --in the first parameter, you can define from the available styles: default, hms, hms-full, timestamp, timestamp-concise "default" to show in HH:MM:SS.sss format. "hms" to show in 1h 2m 3.4s format. "hms-full" is the same as hms but keeps the hours and minutes persistent when they are 0. "timestamp" to show the total time as timestamp 123456.700 format. "timestamp-concise" shows the total time in 123456.7 format (shows and hides decimals depending on availability). + --in the second parameter, you can define whether to show milliseconds, round them or truncate them. Available options: 'truncate' to remove the milliseconds and keep the seconds. 0 to remove the milliseconds and round the seconds. 1 or above is the amount of milliseconds to display. The default value is 3 milliseconds. + --in the third parameter you can define the seperator between hour:minute:second. "default" style is automatically set to ":", "hms", "hms-full" are automatically set to " ". You can define your own. Some examples: ["default", 3, "-"],["hms-full", 5, "."],["hms", "truncate", ":"],["timestamp-concise"],["timestamp", 0],["timestamp", "truncate"],["timestamp", 5] + osd_time_format=[[ + ["default", "truncate"] + ]], + list_time_format=[[ + ["default", "truncate"] + ]], + header_duration_time_format=[[ + ["hms", "truncate", ":"] + ]], + header_length_time_format=[[ + ["hms", "truncate", ":"] + ]], + header_remaining_time_format=[[ + ["hms", "truncate", ":"] + ]], + + -----List Keybind Settings----- + --Add below (after a comma) any additional keybind you want to bind. Or change the letter inside the quotes to change the keybind + --Example of changing and adding keybinds: --From ["b", "B"] To ["b"]. --From [""] to ["alt+b"]. --From [""] to ["a" "ctrl+a", "alt+a"] + list_move_up_keybind=[[ + ["UP", "WHEEL_UP"] + ]], --Keybind that will be used to navigate up on the list + list_move_down_keybind=[[ + ["DOWN", "WHEEL_DOWN"] + ]], --Keybind that will be used to navigate down on the list + list_page_up_keybind=[[ + ["PGUP"] + ]], --Keybind that will be used to go to the first item for the page shown on the list + list_page_down_keybind=[[ + ["PGDWN"] + ]], --Keybind that will be used to go to the last item for the page shown on the list + list_move_first_keybind=[[ + ["HOME"] + ]], --Keybind that will be used to navigate to the first item on the list + list_move_last_keybind=[[ + ["END"] + ]], --Keybind that will be used to navigate to the last item on the list + list_highlight_move_keybind=[[ + ["SHIFT"] + ]], --Keybind that will be used to highlight while pressing a navigational keybind, keep holding shift and then press any navigation keybind, such as: up, down, home, pgdwn, etc.. + list_highlight_all_keybind=[[ + ["ctrl+a", "ctrl+A"] + ]], --Keybind that will be used to highlight all displayed items on the list + list_unhighlight_all_keybind=[[ + ["ctrl+d", "ctrl+D"] + ]], --Keybind that will be used to remove all currently highlighted items from the list + list_select_keybind=[[ + ["ENTER", "MBTN_MID"] + ]], --Keybind that will be used to load entry based on cursor position + list_add_playlist_keybind=[[ + ["CTRL+ENTER"] + ]], --Keybind that will be used to add entry to playlist based on cursor position + list_add_playlist_highlighted_keybind=[[ + ["SHIFT+ENTER"] + ]], --Keybind that will be used to add all highlighted entries to playlist + list_close_keybind=[[ + ["ESC", "MBTN_RIGHT"] + ]], --Keybind that will be used to close the list (closes search first if it is open) + list_delete_keybind=[[ + ["DEL"] + ]], --Keybind that will be used to delete the entry based on cursor position + list_delete_highlighted_keybind=[[ + ["SHIFT+DEL"] + ]], --Keybind that will be used to delete all highlighted entries from the list + list_search_activate_keybind=[[ + ["ctrl+f", "ctrl+F"] + ]], --Keybind that will be used to trigger search + list_search_not_typing_mode_keybind=[[ + ["ALT+ENTER"] + ]], --Keybind that will be used to exit typing mode of search while keeping search open + list_ignored_keybind=[[ + ["B", "b", "k", "K", "c", "C"] + ]], --Keybind thats are ignored when list is open + +---------------------------END OF USER CUSTOMIZATION SETTINGS--------------------------- +} + +(require 'mp.options').read_options(o) +local utils = require 'mp.utils' +local msg = require 'mp.msg' + +o.history_blacklist = utils.parse_json(o.history_blacklist) +o.history_incognito_mode_keybind = utils.parse_json(o.history_incognito_mode_keybind) +o.filters_and_sequence = utils.parse_json(o.filters_and_sequence) +o.keywords_filter_list = utils.parse_json(o.keywords_filter_list) +o.list_filters_sort = utils.parse_json(o.list_filters_sort) +o.logging_protocols = utils.parse_json(o.logging_protocols) +o.history_resume_keybind = utils.parse_json(o.history_resume_keybind) +o.history_load_last_keybind = utils.parse_json(o.history_load_last_keybind) +o.osd_time_format = utils.parse_json(o.osd_time_format) +o.list_time_format = utils.parse_json(o.list_time_format) +o.header_duration_time_format = utils.parse_json(o.header_duration_time_format) +o.header_length_time_format = utils.parse_json(o.header_length_time_format) +o.header_remaining_time_format = utils.parse_json(o.header_remaining_time_format) +o.list_move_up_keybind = utils.parse_json(o.list_move_up_keybind) +o.list_move_down_keybind = utils.parse_json(o.list_move_down_keybind) +o.list_page_up_keybind = utils.parse_json(o.list_page_up_keybind) +o.list_page_down_keybind = utils.parse_json(o.list_page_down_keybind) +o.list_move_first_keybind = utils.parse_json(o.list_move_first_keybind) +o.list_move_last_keybind = utils.parse_json(o.list_move_last_keybind) +o.list_highlight_move_keybind = utils.parse_json(o.list_highlight_move_keybind) +o.list_highlight_all_keybind = utils.parse_json(o.list_highlight_all_keybind) +o.list_unhighlight_all_keybind = utils.parse_json(o.list_unhighlight_all_keybind) +o.list_cycle_sort_keybind = utils.parse_json(o.list_cycle_sort_keybind) +o.list_select_keybind = utils.parse_json(o.list_select_keybind) +o.list_add_playlist_keybind = utils.parse_json(o.list_add_playlist_keybind) +o.list_add_playlist_highlighted_keybind = utils.parse_json(o.list_add_playlist_highlighted_keybind) +o.list_close_keybind = utils.parse_json(o.list_close_keybind) +o.list_delete_keybind = utils.parse_json(o.list_delete_keybind) +o.list_delete_highlighted_keybind = utils.parse_json(o.list_delete_highlighted_keybind) +o.list_search_activate_keybind = utils.parse_json(o.list_search_activate_keybind) +o.list_search_not_typing_mode_keybind = utils.parse_json(o.list_search_not_typing_mode_keybind) +o.next_filter_sequence_keybind = utils.parse_json(o.next_filter_sequence_keybind) +o.previous_filter_sequence_keybind = utils.parse_json(o.previous_filter_sequence_keybind) +o.open_list_keybind = utils.parse_json(o.open_list_keybind) +o.list_filter_jump_keybind = utils.parse_json(o.list_filter_jump_keybind) +o.list_ignored_keybind = utils.parse_json(o.list_ignored_keybind) + +if utils.shared_script_property_set then + utils.shared_script_property_set('simplehistory-menu-open', 'no') +end +mp.set_property('user-data/simplehistory/menu-open', 'no') + +if string.lower(o.log_path) == '/:dir%mpvconf%' then + o.log_path = mp.find_config_file('.') +elseif string.lower(o.log_path) == '/:dir%script%' then + o.log_path = debug.getinfo(1).source:match('@?(.*/)') +elseif o.log_path:match('/:var%%(.*)%%') then + local os_variable = o.log_path:match('/:var%%(.*)%%') + o.log_path = o.log_path:gsub('/:var%%(.*)%%', os.getenv(os_variable)) +end +local log_fullpath = utils.join_path(o.log_path, o.log_file) + +local log_length_text = 'length=' +local log_time_text = 'time=' +local protocols = {'https?:', 'magnet:', 'rtmps?:', 'smb:', 'ftps?:', 'sftp:'} +local available_filters = {'all', 'recents', 'distinct', 'playing', 'protocols', 'fileonly', 'titleonly', 'timeonly', 'keywords'} +local available_sorts = {'added-asc', 'added-desc', 'time-asc', 'time-desc', 'alphanum-asc', 'alphanum-desc'} +local search_string = '' +local search_active = false + +local incognito_mode = false +local autosaved_entry = false +local incognito_auto_run_triggered = false + +local loadTriggered = false --1.1.5# to identify if load is triggered atleast once for idle option +local resume_selected = false +local list_contents = {} +local list_start = 0 +local list_cursor = 1 +local list_highlight_cursor = {} +local list_drawn = false +local list_pages = {} +local filePath, fileTitle, fileLength +local seekTime = 0 +local logTime = 0 --1.3# use logTime since seekTime is used in multiple places +local filterName = 'all' +local sortName + +function starts_protocol(tab, val) + for index, value in ipairs(tab) do + if (val:find(value) == 1) then + return true + end + end + return false +end + +function contain_value(tab, val) + if not tab then return msg.error('check value passed') end + if not val then return msg.error('check value passed') end + + for index, value in ipairs(tab) do + if value.match(string.lower(val), string.lower(value)) then + return true + end + end + + return false +end + +function has_value(tab, val, array2d) + if not tab then return msg.error('check value passed') end + if not val then return msg.error('check value passed') end + if not array2d then + for index, value in ipairs(tab) do + if string.lower(value) == string.lower(val) then + return true + end + end + end + if array2d then + for i=1, #tab do + if tab[i] and string.lower(tab[i][array2d]) == string.lower(val) then + return true + end + end + end + + return false +end + +function file_exists(name) + local f = io.open(name, "r") + if f ~= nil then io.close(f) return true else return false end +end + +function format_time(seconds, sep, decimals, style) + local function divmod (a, b) + return math.floor(a / b), a % b + end + decimals = decimals == nil and 3 or decimals + + local s = seconds + local h, s = divmod(s, 60*60) + local m, s = divmod(s, 60) + + if decimals == 'truncate' then + s = math.floor(s) + decimals = 0 + if style == 'timestamp' then + seconds = math.floor(seconds) + end + end + + if not style or style == '' or style == 'default' then + local second_format = string.format("%%0%d.%df", 2+(decimals > 0 and decimals+1 or 0), decimals) + sep = sep and sep or ":" + return string.format("%02d"..sep.."%02d"..sep..second_format, h, m, s) + elseif style == 'hms' or style == 'hms-full' then + sep = sep ~= nil and sep or " " + if style == 'hms-full' or h > 0 then + return string.format("%dh"..sep.."%dm"..sep.."%." .. tostring(decimals) .. "fs", h, m, s) + elseif m > 0 then + return string.format("%dm"..sep.."%." .. tostring(decimals) .. "fs", m, s) + else + return string.format("%." .. tostring(decimals) .. "fs", s) + end + elseif style == 'timestamp' then + return string.format("%." .. tostring(decimals) .. "f", seconds) + elseif style == 'timestamp-concise' then + return seconds + end +end + +function get_file() + local path = mp.get_property('path') + if not path then return end + + local length = (mp.get_property_number('duration') or 0) + + local title = mp.get_property('media-title'):gsub("\"", "") + + + if starts_protocol(o.logging_protocols, path) and o.prefer_filename_over_title == 'protocols' then + title = mp.get_property('filename'):gsub("\"", "") + elseif not starts_protocol(o.logging_protocols, path) and o.prefer_filename_over_title == 'local' then + title = mp.get_property('filename'):gsub("\"", "") + elseif o.prefer_filename_over_title == 'all' then + title = mp.get_property('filename'):gsub("\"", "") + end + + return path, title, length +end + +function bind_keys(keys, name, func, opts) + if not keys then + mp.add_forced_key_binding(keys, name, func, opts) + return + end + + for i = 1, #keys do + if i == 1 then + mp.add_forced_key_binding(keys[i], name, func, opts) + else + mp.add_forced_key_binding(keys[i], name .. i, func, opts) + end + end +end + +function unbind_keys(keys, name) + if not keys then + mp.remove_key_binding(name) + return + end + + for i = 1, #keys do + if i == 1 then + mp.remove_key_binding(name) + else + mp.remove_key_binding(name .. i) + end + end +end + +function esc_string(str) + return str:gsub("([%p])", "%%%1") +end + +---------Start of LogManager--------- +--LogManager (Read and Format the List from Log)-- +function read_log(func) + local f = io.open(log_fullpath, "r") + if not f then return end + local contents = {} + for line in f:lines() do + table.insert(contents, (func(line))) + end + f:close() + return contents +end + +function read_log_table() + local line_pos = 0 + return read_log(function(line) + local tt, p, t, s, d, n, e, l, dt, ln, r + if line:match('^.-\"(.-)\"') then + tt = line:match('^.-\"(.-)\"') + n, p = line:match('^.-\"(.-)\" | (.*) | ' .. esc_string(log_length_text) .. '(.*)') + else + p = line:match('[(.*)%]]%s(.*) | ' .. esc_string(log_length_text) .. '(.*)') + d, n, e = p:match('^(.-)([^\\/]-)%.([^\\/%.]-)%.?$') + end + dt = line:match('%[(.-)%]') + t = line:match(' | ' .. esc_string(log_time_text) .. '(%d*%.?%d*)(.*)$') + ln = line:match(' | ' .. esc_string(log_length_text) .. '(%d*%.?%d*)(.*)$') + r = tonumber(ln) - tonumber(t) + l = line + line_pos = line_pos + 1 + return {found_path = p, found_time = t, found_name = n, found_title = tt, found_line = l, found_sequence = line_pos, found_directory = d, found_datetime = dt, found_length = ln, found_remaining = r} + end) +end + +function list_sort(tab, sort) + if sort == 'added-asc' then + table.sort(tab, function(a, b) return a['found_sequence'] < b['found_sequence'] end) + elseif sort == 'added-desc' then + table.sort(tab, function(a, b) return a['found_sequence'] > b['found_sequence'] end) + elseif sort == 'time-asc' then + table.sort(tab, function(a, b) return tonumber(a['found_time']) > tonumber(b['found_time']) end) + elseif sort == 'time-desc' then + table.sort(tab, function(a, b) return tonumber(a['found_time']) < tonumber(b['found_time']) end) + elseif sort == 'alphanum-asc' or sort == 'alphanum-desc' then + local function padnum(d) local dec, n = string.match(d, "(%.?)0*(.+)") + return #dec > 0 and ("%.12f"):format(d) or ("%s%03d%s"):format(dec, #n, n) end + if sort == 'alphanum-asc' then + table.sort(tab, function(a, b) return tostring(a['found_path']):gsub("%.?%d+", padnum) .. ("%3d"):format(#b) > tostring(b['found_path']):gsub("%.?%d+", padnum) .. ("%3d"):format(#a) end) + elseif sort == 'alphanum-desc' then + table.sort(tab, function(a, b) return tostring(a['found_path']):gsub("%.?%d+", padnum) .. ("%3d"):format(#b) < tostring(b['found_path']):gsub("%.?%d+", padnum) .. ("%3d"):format(#a) end) + end + end + + return tab +end + +function parse_header(string) + local osd_header_color = string.format("{\\1c&H%s}", o.header_color) + local osd_search_color = osd_header_color + if search_active == 'typing' then + osd_search_color = string.format("{\\1c&H%s}", o.search_color_typing) + elseif search_active == 'not_typing' then + osd_search_color = string.format("{\\1c&H%s}", o.search_color_not_typing) + end + local osd_msg_end = "{\\1c&HFFFFFF}" + + string = string:gsub("%%total%%", #list_contents) + :gsub("%%cursor%%", list_cursor) + + if filterName ~= 'all' then + string = string:gsub("%%filter%%", filterName) + :gsub("%%prefilter%%", o.header_filter_pre_text) + :gsub("%%afterfilter%%", o.header_filter_after_text) + else + string = string:gsub("%%filter%%", '') + :gsub("%%prefilter%%", '') + :gsub("%%afterfilter%%", '') + end + + local list_total_duration = 0 + if string:match('%listduration%%') then + list_total_duration = get_total_duration('found_time') + if list_total_duration > 0 then + string = string:gsub("%%listduration%%", format_time(list_total_duration, o.header_duration_time_format[3], o.header_duration_time_format[2], o.header_duration_time_format[1])) + else + string = string:gsub("%%listduration%%", '') + end + end + if list_total_duration > 0 then + string = string:gsub("%%prelistduration%%", o.header_list_duration_pre_text) + :gsub("%%afterlistduration%%", o.header_list_duration_after_text) + else + string = string:gsub("%%prelistduration%%", '') + :gsub("%%afterlistduration%%", '') + end + + local list_total_length = 0 + if string:match('%listlength%%') then + list_total_length = get_total_duration('found_length') + if list_total_length > 0 then + string = string:gsub("%%listlength%%", format_time(list_total_length, o.header_length_time_format[3], o.header_length_time_format[2], o.header_length_time_format[1])) + else + string = string:gsub("%%listlength%%", '') + end + end + if list_total_length > 0 then + string = string:gsub("%%prelistlength%%", o.header_list_length_pre_text) + :gsub("%%afterlistlength%%", o.header_list_length_after_text) + else + string = string:gsub("%%prelistlength%%", '') + :gsub("%%afterlistlength%%", '') + end + + local list_total_remaining = 0 + if string:match('%listremaining%%') then + list_total_remaining = get_total_duration('found_remaining') + if list_total_remaining > 0 then + string = string:gsub("%%listremaining%%", format_time(list_total_remaining, o.header_remaining_time_format[3], o.header_remaining_time_format[2], o.header_remaining_time_format[1])) + else + string = string:gsub("%%listremaining%%", '') + end + end + if list_total_remaining > 0 then + string = string:gsub("%%prelistremaining%%", o.header_list_remaining_pre_text) + :gsub("%%afterlistremaining%%", o.header_list_remaining_after_text) + else + string = string:gsub("%%prelistremaining%%", '') + :gsub("%%afterlistremaining%%", '') + end + + if #list_highlight_cursor > 0 then + string = string:gsub("%%highlight%%", #list_highlight_cursor) + :gsub("%%prehighlight%%", o.header_highlight_pre_text) + :gsub("%%afterhighlight%%", o.header_highlight_after_text) + else + string = string:gsub("%%highlight%%", '') + :gsub("%%prehighlight%%", '') + :gsub("%%afterhighlight%%", '') + end + + if sortName and sortName ~= o.header_sort_hide_text then + string = string:gsub("%%sort%%", sortName) + :gsub("%%presort%%", o.header_sort_pre_text) + :gsub("%%aftersort%%", o.header_sort_after_text) + else + string = string:gsub("%%sort%%", '') + :gsub("%%presort%%", '') + :gsub("%%aftersort%%", '') + end + + if search_active then + local search_string_osd = search_string + if search_string_osd ~= '' then + search_string_osd = search_string:gsub('%%', '%%%%%%%%'):gsub('\\', '\\​'):gsub('{', '\\{') + end + + string = string:gsub("%%search%%", osd_search_color..search_string_osd..osd_header_color) + :gsub("%%presearch%%", o.header_search_pre_text) + :gsub("%%aftersearch%%", o.header_search_after_text) + else + string = string:gsub("%%search%%", '') + :gsub("%%presearch%%", '') + :gsub("%%aftersearch%%", '') + end + string = string:gsub("%%%%", "%%") + return string +end + +function get_list_contents(filter, sort) + if not filter then filter = filterName end + if not sort then sort = get_list_sort(filter) end + + local current_sort + + local filtered_table = {} + + local prev_list_contents + if list_contents ~= nil and list_contents[1] then + prev_list_contents = list_contents + else + prev_list_contents = read_log_table() + end + + list_contents = read_log_table() + if not list_contents and not search_active or not list_contents[1] and not search_active then return end + current_sort = 'added-asc' + + if filter == 'recents' then + table.sort(list_contents, function(a, b) return a['found_sequence'] < b['found_sequence'] end) + local unique_values = {} + local list_total = #list_contents + + if filePath == list_contents[#list_contents].found_path and tonumber(list_contents[#list_contents].found_time) == 0 then + list_total = list_total -1 + end + + for i = list_total, 1, -1 do + if not has_value(unique_values, list_contents[i].found_path) then + table.insert(unique_values, list_contents[i].found_path) + table.insert(filtered_table, list_contents[i]) + end + end + table.sort(filtered_table, function(a, b) return a['found_sequence'] < b['found_sequence'] end) + + list_contents = filtered_table + + end + + if filter == 'distinct' then + table.sort(list_contents, function(a, b) return a['found_sequence'] < b['found_sequence'] end) + local unique_values = {} + local list_total = #list_contents + + if filePath == list_contents[#list_contents].found_path and tonumber(list_contents[#list_contents].found_time) == 0 then + list_total = list_total -1 + end + + for i = list_total, 1, -1 do + if list_contents[i].found_directory and not has_value(unique_values, list_contents[i].found_directory) and not starts_protocol(protocols, list_contents[i].found_path) then + table.insert(unique_values, list_contents[i].found_directory) + table.insert(filtered_table, list_contents[i]) + end + end + table.sort(filtered_table, function(a, b) return a['found_sequence'] < b['found_sequence'] end) + + list_contents = filtered_table + end + + if filter == 'fileonly' then + for i = 1, #list_contents do + if tonumber(list_contents[i].found_time) == 0 then + table.insert(filtered_table, list_contents[i]) + end + end + + list_contents = filtered_table + end + + if filter == 'timeonly' then + for i = 1, #list_contents do + if tonumber(list_contents[i].found_time) > 0 then + table.insert(filtered_table, list_contents[i]) + end + end + + list_contents = filtered_table + end + + if filter == 'titleonly' then + for i = 1, #list_contents do + if list_contents[i].found_title then + table.insert(filtered_table, list_contents[i]) + end + end + + list_contents = filtered_table + end + + if filter == 'protocols' then + for i = 1, #list_contents do + if starts_protocol(o.logging_protocols, list_contents[i].found_path) then + table.insert(filtered_table, list_contents[i]) + end + end + + list_contents = filtered_table + end + + if filter == 'keywords' then + for i = 1, #list_contents do + if contain_value(o.keywords_filter_list, list_contents[i].found_line) then + table.insert(filtered_table, list_contents[i]) + end + end + + list_contents = filtered_table + end + + if filter == 'playing' then + for i = 1, #list_contents do + if list_contents[i].found_path == filePath then + table.insert(filtered_table, list_contents[i]) + end + end + + list_contents = filtered_table + end + + if search_active and search_string ~= '' then + local filtered_table = {} + + local search_query = '' + for search in search_string:gmatch("[^%s]+") do + search_query = search_query..'.-'..esc_string(search) + end + + local contents_string = '' + for i = 1, #list_contents do + + if o.search_behavior == 'specific' then + if string.lower(list_contents[i].found_path):match(string.lower(search_query)) then + table.insert(filtered_table, list_contents[i]) + elseif list_contents[i].found_title and string.lower(list_contents[i].found_title):match(string.lower(search_query)) then + table.insert(filtered_table, list_contents[i]) + elseif tonumber(list_contents[i].found_time) > 0 and format_time(list_contents[i].found_time, o.osd_time_format[3], o.osd_time_format[2], o.osd_time_format[1]):match(search_query) then + table.insert(filtered_table, list_contents[i]) + elseif string.lower(list_contents[i].found_datetime):match(string.lower(search_query)) then + table.insert(filtered_table, list_contents[i]) + end + elseif o.search_behavior == 'any' then + contents_string = list_contents[i].found_datetime..(list_contents[i].found_title or '')..list_contents[i].found_path + if tonumber(list_contents[i].found_time) > 0 then + contents_string = contents_string..format_time(list_contents[i].found_time, o.osd_time_format[3], o.osd_time_format[2], o.osd_time_format[1]) + end + elseif o.search_behavior == 'any-notime' then + contents_string = list_contents[i].found_datetime..(list_contents[i].found_title or '')..list_contents[i].found_path + end + + if string.lower(contents_string):match(string.lower(search_query)) then + table.insert(filtered_table, list_contents[i]) + end + end + + list_contents = filtered_table + + end + + if sort ~= current_sort then + list_sort(list_contents, sort) + end + + if not list_contents and not search_active or not list_contents[1] and not search_active then return end +end + +function get_list_sort(filter) + if not filter then filter = filterName end + + local sort + for i=1, #o.list_filters_sort do + if o.list_filters_sort[i][1] == filter then + if has_value(available_sorts, o.list_filters_sort[i][2]) then sort = o.list_filters_sort[i][2] end + break + end + end + + if not sort and has_value(available_sorts, o.list_default_sort) then sort = o.list_default_sort end + + if not sort then sort = 'added-asc' end + + return sort +end + +function draw_list() + local osd_msg = '' + local osd_index = '' + local osd_key = '' + local osd_color = '' + local key = 0 + local osd_text = string.format("{\\an%f{\\fscx%f}{\\fscy%f}{\\bord%f}{\\1c&H%s}", o.list_alignment, o.text_scale, o.text_scale, o.text_border, o.text_color) + local osd_cursor = string.format("{\\an%f}{\\fscx%f}{\\fscy%f}{\\bord%f}{\\1c&H%s}", o.list_alignment, o.text_cursor_scale, o.text_cursor_scale, o.text_cursor_border, o.text_cursor_color) + local osd_header = string.format("{\\an%f}{\\fscx%f}{\\fscy%f}{\\bord%f}{\\1c&H%s}", o.list_alignment, o.header_scale, o.header_scale, o.header_border, o.header_color) + local osd_msg_end = "{\\1c&HFFFFFF}" + local osd_time_type = 'found_time' + + if o.text_time_type == 'length' then + osd_time_type = 'found_length' + elseif o.text_time_type == 'remaining' then + osd_time_type = 'found_remaining' + end + + if o.header_text ~= '' then + osd_msg = osd_msg .. osd_header .. parse_header(o.header_text) + osd_msg = osd_msg .. "\\h\\N\\N" .. osd_msg_end + end + + if search_active and not list_contents[1] then + osd_msg = osd_msg .. 'No search results found' .. osd_msg_end + end + + if o.list_middle_loader then + list_start = list_cursor - math.floor(o.list_show_amount / 2) + else + list_start = list_cursor - o.list_show_amount + end + + local showall = false + local showrest = false + if list_start < 0 then list_start = 0 end + if #list_contents <= o.list_show_amount then + list_start = 0 + showall = true + end + if list_start > math.max(#list_contents - o.list_show_amount - 1, 0) then + list_start = #list_contents - o.list_show_amount + showrest = true + end + if list_start > 0 and not showall then + osd_msg = osd_msg .. o.list_sliced_prefix .. osd_msg_end + end + for i = list_start, list_start + o.list_show_amount - 1, 1 do + if i == #list_contents then break end + + if o.show_paths then + p = list_contents[#list_contents - i].found_path or list_contents[#list_contents - i].found_name or "" + else + p = list_contents[#list_contents - i].found_name or list_contents[#list_contents - i].found_path or "" + end + + if o.slice_longfilenames and p:len() > o.slice_longfilenames_amount then + p = p:sub(1, o.slice_longfilenames_amount) .. "..." + end + + if o.quickselect_0to9_keybind and o.list_show_amount <= 10 and o.quickselect_0to9_pre_text then + key = 1 + key + if key == 10 then key = 0 end + osd_key = '(' .. key .. ') ' + end + + if o.show_item_number then + osd_index = (i + 1) .. '. ' + end + + if i + 1 == list_cursor then + osd_color = osd_cursor + else + osd_color = osd_text + end + + for j = 1, #list_highlight_cursor do + if list_highlight_cursor[j] and list_highlight_cursor[j][1] == i+1 then + osd_msg = osd_msg..osd_color..esc_string(o.text_highlight_pre_text) + end + end + + osd_msg = osd_msg .. osd_color .. osd_key .. osd_index .. p + + if list_contents[#list_contents - i][osd_time_type] and tonumber(list_contents[#list_contents - i][osd_time_type]) > 0 then + osd_msg = osd_msg .. o.time_seperator .. format_time(list_contents[#list_contents - i][osd_time_type], o.list_time_format[3], o.list_time_format[2], o.list_time_format[1]) + end + + osd_msg = osd_msg .. '\\h\\N\\N' .. osd_msg_end + + if i == list_start + o.list_show_amount - 1 and not showall and not showrest then + osd_msg = osd_msg .. o.list_sliced_suffix + end + + end + mp.set_osd_ass(0, 0, osd_msg) +end + +function list_empty_error_msg() + if list_contents ~= nil and list_contents[1] then return end + local msg_text + if filterName ~= 'all' then + msg_text = filterName .. " filter in History Empty" + else + msg_text = "History Empty" + end + msg.info(msg_text) + if o.osd_messages == true and not list_drawn then + mp.osd_message(msg_text) + end +end + +function display_list(filter, sort, action) + if not filter or not has_value(available_filters, filter) then filter = 'all' end + if not sortName then sortName = get_list_sort(filter) end + + local prev_sort = sortName + if not has_value(available_sorts, prev_sort) then prev_sort = get_list_sort() end + + if not sort then sort = get_list_sort(filter) end + sortName = sort + + local prev_filter = filterName + filterName = filter + + get_list_contents(filter, sort) + + if action ~= 'hide-osd' then + if not list_contents or not list_contents[1] then + list_empty_error_msg() + filterName = prev_filter + get_list_contents(filterName) + return + end + end + if not list_contents and not search_active or not list_contents[1] and not search_active then return end + + if not has_value(o.filters_and_sequence, filter) then + table.insert(o.filters_and_sequence, filter) + end + + local insert_new = false + + local trigger_close_list = false + local trigger_initial_list = false + + + if not list_pages or not list_pages[1] then + table.insert(list_pages, {filter, 1, 1, {}, sort}) + else + for i = 1, #list_pages do + if list_pages[i][1] == filter then + list_pages[i][3] = list_pages[i][3]+1 + insert_new = false + break + else + insert_new = true + end + end + end + + if insert_new then table.insert(list_pages, {filter, 1, 1, {}, sort}) end + + for i = 1, #list_pages do + if not search_active and list_pages[i][1] == prev_filter then + list_pages[i][2] = list_cursor + list_pages[i][4] = list_highlight_cursor + list_pages[i][5] = prev_sort + end + if list_pages[i][1] ~= filter then + list_pages[i][3] = 0 + end + if list_pages[i][3] == 2 and filter == 'all' and o.main_list_keybind_twice_exits then + trigger_close_list = true + elseif list_pages[i][3] == 2 and list_pages[1][1] == filter then + trigger_close_list = true + elseif list_pages[i][3] == 2 then + trigger_initial_list = true + end + end + + if trigger_initial_list then + display_list(list_pages[1][1], nil, 'hide-osd') + return + end + + if trigger_close_list then + list_close_and_trash_collection() + return + end + + if not search_active then get_page_properties(filter) else update_search_results('','') end + draw_list() + if utils.shared_script_property_set then + utils.shared_script_property_set('simplehistory-menu-open', 'yes') + end + mp.set_property('user-data/simplehistory/menu-open', 'yes') + if o.toggle_idlescreen then mp.commandv('script-message', 'osc-idlescreen', 'no', 'no_osd') end --1.1.6# fix osc-idlescreen (value was yes for some reason) + list_drawn = true + if not search_active then get_list_keybinds() end +end + +--End of LogManager (Read and Format the List from Log)-- + +--LogManager Navigation-- +function select(pos, action) + if not search_active then + if not list_contents or not list_contents[1] then + list_close_and_trash_collection() + return + end + end + + local list_cursor_temp = list_cursor + pos + if list_cursor_temp > 0 and list_cursor_temp <= #list_contents then + list_cursor = list_cursor_temp + + if action == 'highlight' then + if not has_value(list_highlight_cursor, list_cursor, 1) then + if pos > -1 then + for i = pos, 1, -1 do + if not has_value(list_highlight_cursor, list_cursor-i, 1) then + table.insert(list_highlight_cursor, {list_cursor-i, list_contents[#list_contents+1+i - list_cursor]}) + end + end + else + for i = pos, -1, 1 do + if not has_value(list_highlight_cursor, list_cursor-i, 1) then + table.insert(list_highlight_cursor, {list_cursor-i, list_contents[#list_contents+1+i - list_cursor]}) + end + end + end + table.insert(list_highlight_cursor, {list_cursor, list_contents[#list_contents+1 - list_cursor]}) + else + for i=1, #list_highlight_cursor do + if list_highlight_cursor[i] and list_highlight_cursor[i][1] == list_cursor then + table.remove(list_highlight_cursor, i) + end + end + if pos > -1 then + for i=1, #list_highlight_cursor do + for j = pos, 1, -1 do + if list_highlight_cursor[i] and list_highlight_cursor[i][1] == list_cursor-j then + table.remove(list_highlight_cursor, i) + end + end + end + else + for i=#list_highlight_cursor, 1, -1 do + for j = pos, -1, 1 do + if list_highlight_cursor[i] and list_highlight_cursor[i][1] == list_cursor-j then + table.remove(list_highlight_cursor, i) + end + end + end + end + end + end + end + + if o.loop_through_list then + if list_cursor_temp > #list_contents then + list_cursor = 1 + elseif list_cursor_temp < 1 then + list_cursor = #list_contents + end + end + + draw_list() +end + +function list_move_up(action) + select(-1, action) + + if search_active and o.search_not_typing_smartly then + list_search_not_typing_mode(true) + end +end + +function list_move_down(action) + select(1, action) + + if search_active and o.search_not_typing_smartly then + list_search_not_typing_mode(true) + end +end + +function list_move_first(action) + select(1 - list_cursor, action) + + if search_active and o.search_not_typing_smartly then + list_search_not_typing_mode(true) + end +end + +function list_move_last(action) + select(#list_contents - list_cursor, action) + + if search_active and o.search_not_typing_smartly then + list_search_not_typing_mode(true) + end +end + +function list_page_up(action) + select(list_start + 1 - list_cursor, action) + + if search_active and o.search_not_typing_smartly then + list_search_not_typing_mode(true) + end +end + +function list_page_down(action) + if o.list_middle_loader then + if #list_contents < o.list_show_amount then + select(#list_contents - list_cursor, action) + else + select(o.list_show_amount + list_start - list_cursor, action) + end + else + if o.list_show_amount > list_cursor then + select(o.list_show_amount - list_cursor, action) + elseif #list_contents - list_cursor >= o.list_show_amount then + select(o.list_show_amount, action) + else + select(#list_contents - list_cursor, action) + end + end + + if search_active and o.search_not_typing_smartly then + list_search_not_typing_mode(true) + end +end + +function list_highlight_all() + get_list_contents(filterName) + if not list_contents or not list_contents[1] then return end + + if #list_highlight_cursor < #list_contents then + for i=1, #list_contents do + if not has_value(list_highlight_cursor, i, 1) then + table.insert(list_highlight_cursor, {i, list_contents[#list_contents+1-i]}) + end + end + select(0) + else + list_unhighlight_all() + end +end + +function list_unhighlight_all() + if not list_highlight_cursor or not list_highlight_cursor[1] then return end + list_highlight_cursor = {} + select(0) +end +--End of LogManager Navigation-- + +--LogManager Actions-- +function load(list_cursor, add_playlist, target_time) + if not list_contents or not list_contents[1] then return end + if not target_time then + seekTime = tonumber(list_contents[#list_contents - list_cursor + 1].found_time) + o.resume_offset + if (seekTime < 0) then + seekTime = 0 + end + else + seekTime = target_time + end + if file_exists(list_contents[#list_contents - list_cursor + 1].found_path) or starts_protocol(protocols, list_contents[#list_contents - list_cursor + 1].found_path) then + if not add_playlist then + if filePath ~= list_contents[#list_contents - list_cursor + 1].found_path then + mp.commandv('loadfile', list_contents[#list_contents - list_cursor + 1].found_path) + resume_selected = true + else + mp.commandv('seek', seekTime, 'absolute', 'exact') + list_close_and_trash_collection() + end + if o.osd_messages == true then + mp.osd_message('Loaded:\n' .. list_contents[#list_contents - list_cursor + 1].found_name.. o.time_seperator .. format_time(seekTime, o.osd_time_format[3], o.osd_time_format[2], o.osd_time_format[1])) + end + msg.info('Loaded the below file:\n' .. list_contents[#list_contents - list_cursor + 1].found_name .. ' | '.. format_time(seekTime)) + else + mp.commandv('loadfile', list_contents[#list_contents - list_cursor + 1].found_path, 'append-play') + if o.osd_messages == true then + mp.osd_message('Added into Playlist:\n'..list_contents[#list_contents - list_cursor + 1].found_name..' ') + end + msg.info('Added the below file into playlist:\n' .. list_contents[#list_contents - list_cursor + 1].found_path) + end + else + if o.osd_messages == true then + mp.osd_message('File Doesn\'t Exist:\n' .. list_contents[#list_contents - list_cursor + 1].found_path) + end + msg.info('The file below doesn\'t seem to exist:\n' .. list_contents[#list_contents - list_cursor + 1].found_path) + return + end +end + +function list_select() + load(list_cursor) +end + +function list_add_playlist(action) + if not action then + load(list_cursor, true) + elseif action == 'highlight' then + if not list_highlight_cursor or not list_highlight_cursor[1] then return end + local file_ignored_total = 0 + + for i=1, #list_highlight_cursor do + if file_exists(list_highlight_cursor[i][2].found_path) or starts_protocol(protocols, list_highlight_cursor[i][2].found_path) then + mp.commandv("loadfile", list_highlight_cursor[i][2].found_path, "append-play") + else + msg.warn('The below file was not added into playlist as it does not seem to exist:\n' .. list_highlight_cursor[i][2].found_path) + file_ignored_total = file_ignored_total + 1 + end + end + if o.osd_messages == true then + if file_ignored_total > 0 then + mp.osd_message('Added into Playlist '..#list_highlight_cursor - file_ignored_total..' Item/s\nIgnored '..file_ignored_total.. " Item/s That Do Not Exist") + else + mp.osd_message('Added into Playlist '..#list_highlight_cursor - file_ignored_total..' Item/s') + end + end + if file_ignored_total > 0 then + msg.warn('Ignored a total of '..file_ignored_total.. " Item/s that does not seem to exist") + end + msg.info('Added into playlist a total of '..#list_highlight_cursor - file_ignored_total..' item/s') + end +end + +function delete_log_entry_specific(target_index, target_path, target_time) + local trigger_delete = false + list_contents = read_log_table() + if not list_contents or not list_contents[1] then return end + if target_index == 'last' then target_index = #list_contents end + if not target_index then return end + + if target_index and target_path and target_time then + if list_contents[target_index].found_path == target_path and tonumber(list_contents[target_index].found_time) == target_time then + table.remove(list_contents, target_index) + trigger_delete = true + end + elseif target_index and target_path and not target_time then + if list_contents[target_index].found_path == target_path then + table.remove(list_contents, target_index) + trigger_delete = true + end + elseif target_index and target_time and not target_path then + if tonumber(list_contents[target_index].found_time) == target_time then + table.remove(list_contents, target_index) + trigger_delete = true + end + elseif target_index and not target_path and not target_time then + table.remove(list_contents, target_index) + trigger_delete = true + end + + if not trigger_delete then return end + local f = io.open(log_fullpath, "w+") + if list_contents ~= nil and list_contents[1] then + for i = 1, #list_contents do + f:write(("%s\n"):format(list_contents[i].found_line)) + end + end + f:close() +end + +function delete_log_entry(multiple, round, target_path, target_time, entry_limit) + if not target_path then target_path = filePath end + if not target_time then target_time = seekTime end + list_contents = read_log_table() + if not list_contents or not list_contents[1] then return end + local trigger_delete = false + + if not multiple then + for i = #list_contents, 1, -1 do + if not round then + if list_contents[i].found_path == target_path and tonumber(list_contents[i].found_time) == target_time then + table.remove(list_contents, i) + trigger_delete = true + break + end + else + if list_contents[i].found_path == target_path and math.floor(tonumber(list_contents[i].found_time)) == target_time then + table.remove(list_contents, i) + trigger_delete = true + break + end + end + end + else + for i = #list_contents, 1, -1 do + if not round then + if list_contents[i].found_path == target_path and tonumber(list_contents[i].found_time) == target_time then + table.remove(list_contents, i) + trigger_delete = true + end + else + if list_contents[i].found_path == target_path and math.floor(tonumber(list_contents[i].found_time)) == target_time then + table.remove(list_contents, i) + trigger_delete = true + end + end + end + end + + if entry_limit and entry_limit > -1 then + local entries_found = 0 + for i = #list_contents, 1, -1 do + if list_contents[i].found_path == target_path and entries_found < entry_limit then + entries_found = entries_found + 1 + elseif list_contents[i].found_path == target_path and entries_found >= entry_limit then + table.remove(list_contents,i) + trigger_delete = true + end + end + end + + if not trigger_delete then return end + local f = io.open(log_fullpath, "w+") + if list_contents ~= nil and list_contents[1] then + for i = 1, #list_contents do + f:write(("%s\n"):format(list_contents[i].found_line)) + end + end + f:close() +end + +function delete_log_entry_highlighted() + if not list_highlight_cursor or not list_highlight_cursor[1] then return end + list_contents = read_log_table() + if not list_contents or not list_contents[1] then return end + + local list_contents_length = #list_contents + + for i = 1, list_contents_length do + for j=1, #list_highlight_cursor do + if list_contents[list_contents_length+1-i] then + if list_contents[list_contents_length+1-i].found_sequence == list_highlight_cursor[j][2].found_sequence then + table.remove(list_contents, list_contents_length+1-i) + end + end + end + end + + msg.info("Deleted "..#list_highlight_cursor.." Item/s") + + list_unhighlight_all() + + local f = io.open(log_fullpath, "w+") + if list_contents ~= nil and list_contents[1] then + for i = 1, #list_contents do + f:write(("%s\n"):format(list_contents[i].found_line)) + end + end + f:close() + +end + +function delete_selected() + filePath = list_contents[#list_contents - list_cursor + 1].found_path + fileTitle = list_contents[#list_contents - list_cursor + 1].found_name + seekTime = tonumber(list_contents[#list_contents - list_cursor + 1].found_time) + if not filePath and not seekTime then + msg.info("Failed to delete") + return + end + delete_log_entry() + msg.info("Deleted \"" .. filePath .. "\" | " .. format_time(seekTime)) + filePath, fileTitle, fileLength = get_file() +end + +function list_delete(action) + if not action then + delete_selected() + elseif action == 'highlight' then + delete_log_entry_highlighted() + end + get_list_contents() + if not list_contents or not list_contents[1] then + list_close_and_trash_collection() + return + end + if list_cursor < #list_contents + 1 then + select(0) + else + list_move_last() + end +end + +function get_total_duration(action) + if not list_contents or not list_contents[1] then return 0 end + local list_total_duration = 0 + if action == 'found_time' or action == 'found_length' or action == 'found_remaining' then + for i = #list_contents, 1, -1 do + if tonumber(list_contents[i][action]) > 0 then + list_total_duration = list_total_duration + list_contents[i][action] + end + end + end + return list_total_duration +end + +function list_cycle_sort() + local next_sort + for i = 1, #available_sorts do + if sortName == available_sorts[i] then + if i == #available_sorts then + next_sort = available_sorts[1] + break + else + next_sort = available_sorts[i+1] + break + end + end + end + if not next_sort then return end + get_list_contents(filterName, next_sort) + sortName = next_sort + update_list_highlist_cursor() + select(0) +end + +function update_list_highlist_cursor() + if not list_highlight_cursor or not list_highlight_cursor[1] then return end + + local temp_list_highlight_cursor = {} + for i = 1, #list_contents do + for j=1, #list_highlight_cursor do + if list_contents[#list_contents+1-i].found_sequence == list_highlight_cursor[j][2].found_sequence then + table.insert(temp_list_highlight_cursor, {i, list_highlight_cursor[j][2]}) + end + end + end + + list_highlight_cursor = temp_list_highlight_cursor +end + +--End of LogManager Actions-- + +--LogManager Filter Functions-- +function get_page_properties(filter) + if not filter then return end + for i=1, #list_pages do + if list_pages[i][1] == filter then + list_cursor = list_pages[i][2] + list_highlight_cursor = list_pages[i][4] + sortName = list_pages[i][5] + end + end + if list_cursor > #list_contents then + list_move_last() + end +end + +function select_filter_sequence(pos) + if not list_drawn then return end + local curr_pos + local target_pos + + for i = 1, #o.filters_and_sequence do + if filterName == o.filters_and_sequence[i] then + curr_pos = i + end + end + + if curr_pos and pos > -1 then + for i = curr_pos, #o.filters_and_sequence do + if o.filters_and_sequence[i + pos] then + get_list_contents(o.filters_and_sequence[i + pos]) + if list_contents ~= nil and list_contents[1] then + target_pos = i + pos + break + end + end + end + elseif curr_pos and pos < 0 then + for i = curr_pos, 0, -1 do + if o.filters_and_sequence[i + pos] then + get_list_contents(o.filters_and_sequence[i + pos]) + if list_contents ~= nil and list_contents[1] then + target_pos = i + pos + break + end + end + end + end + + if o.loop_through_filters then + if not target_pos and pos > -1 or target_pos and target_pos > #o.filters_and_sequence then + for i = 1, #o.filters_and_sequence do + get_list_contents(o.filters_and_sequence[i]) + if list_contents ~= nil and list_contents[1] then + target_pos = i + break + end + end + end + if not target_pos and pos < 0 or target_pos and target_pos < 1 then + for i = #o.filters_and_sequence, 1, -1 do + get_list_contents(o.filters_and_sequence[i]) + if list_contents ~= nil and list_contents[1] then + target_pos = i + break + end + end + end + end + + if o.filters_and_sequence[target_pos] then + display_list(o.filters_and_sequence[target_pos], nil, 'hide-osd') + end +end + +function list_filter_next() + select_filter_sequence(1) +end +function list_filter_previous() + select_filter_sequence(-1) +end +--End of LogManager Filter Functions-- + +--LogManager (List Bind and Unbind)-- +function get_list_keybinds() + bind_keys(o.list_ignored_keybind, 'ignore') + bind_keys(o.list_move_up_keybind, 'move-up', list_move_up, 'repeatable') + bind_keys(o.list_move_down_keybind, 'move-down', list_move_down, 'repeatable') + bind_keys(o.list_move_first_keybind, 'move-first', list_move_first, 'repeatable') + bind_keys(o.list_move_last_keybind, 'move-last', list_move_last, 'repeatable') + bind_keys(o.list_page_up_keybind, 'page-up', list_page_up, 'repeatable') + bind_keys(o.list_page_down_keybind, 'page-down', list_page_down, 'repeatable') + bind_keys(o.list_select_keybind, 'list-select', list_select) + bind_keys(o.list_add_playlist_keybind, 'list-add-playlist', list_add_playlist) + bind_keys(o.list_add_playlist_highlighted_keybind, 'list-add-playlist-highlight', function()list_add_playlist('highlight')end) + bind_keys(o.list_delete_keybind, 'list-delete', list_delete) + bind_keys(o.list_delete_highlighted_keybind, 'list-delete-highlight', function()list_delete('highlight')end) + bind_keys(o.next_filter_sequence_keybind, 'list-filter-next', list_filter_next) + bind_keys(o.previous_filter_sequence_keybind, 'list-filter-previous', list_filter_previous) + bind_keys(o.list_search_activate_keybind, 'list-search-activate', list_search_activate) + bind_keys(o.list_highlight_all_keybind, 'list-highlight-all', list_highlight_all) + bind_keys(o.list_unhighlight_all_keybind, 'list-unhighlight-all', list_unhighlight_all) + bind_keys(o.list_cycle_sort_keybind, 'list-cycle-sort', list_cycle_sort) + + for i = 1, #o.list_highlight_move_keybind do + for j = 1, #o.list_move_up_keybind do + mp.add_forced_key_binding(o.list_highlight_move_keybind[i]..'+'..o.list_move_up_keybind[j], 'highlight-move-up'..j, function()list_move_up('highlight') end, 'repeatable') + end + for j = 1, #o.list_move_down_keybind do + mp.add_forced_key_binding(o.list_highlight_move_keybind[i]..'+'..o.list_move_down_keybind[j], 'highlight-move-down'..j, function()list_move_down('highlight') end, 'repeatable') + end + for j = 1, #o.list_move_first_keybind do + mp.add_forced_key_binding(o.list_highlight_move_keybind[i]..'+'..o.list_move_first_keybind[j], 'highlight-move-first'..j, function()list_move_first('highlight') end, 'repeatable') + end + for j = 1, #o.list_move_last_keybind do + mp.add_forced_key_binding(o.list_highlight_move_keybind[i]..'+'..o.list_move_last_keybind[j], 'highlight-move-last'..j, function()list_move_last('highlight') end, 'repeatable') + end + for j = 1, #o.list_page_up_keybind do + mp.add_forced_key_binding(o.list_highlight_move_keybind[i]..'+'..o.list_page_up_keybind[j], 'highlight-page-up'..j, function()list_page_up('highlight') end, 'repeatable') + end + for j = 1, #o.list_page_down_keybind do + mp.add_forced_key_binding(o.list_highlight_move_keybind[i]..'+'..o.list_page_down_keybind[j], 'highlight-page-down'..j, function()list_page_down('highlight') end, 'repeatable') + end + end + + if not search_active then + bind_keys(o.list_close_keybind, 'list-close', list_close_and_trash_collection) + end + + for i = 1, #o.list_filter_jump_keybind do + mp.add_forced_key_binding(o.list_filter_jump_keybind[i][1], 'list-filter-jump'..i, function()display_list(o.list_filter_jump_keybind[i][2]) end) + end + + for i = 1, #o.open_list_keybind do + if i == 1 then + mp.remove_key_binding('open-list') + else + mp.remove_key_binding('open-list'..i) + end + end + + if o.quickselect_0to9_keybind and o.list_show_amount <= 10 then + mp.add_forced_key_binding("1", "recent-1", function()load(list_start + 1) end) + mp.add_forced_key_binding("2", "recent-2", function()load(list_start + 2) end) + mp.add_forced_key_binding("3", "recent-3", function()load(list_start + 3) end) + mp.add_forced_key_binding("4", "recent-4", function()load(list_start + 4) end) + mp.add_forced_key_binding("5", "recent-5", function()load(list_start + 5) end) + mp.add_forced_key_binding("6", "recent-6", function()load(list_start + 6) end) + mp.add_forced_key_binding("7", "recent-7", function()load(list_start + 7) end) + mp.add_forced_key_binding("8", "recent-8", function()load(list_start + 8) end) + mp.add_forced_key_binding("9", "recent-9", function()load(list_start + 9) end) + mp.add_forced_key_binding("0", "recent-0", function()load(list_start + 10) end) + end +end + +function unbind_list_keys() + unbind_keys(o.list_ignored_keybind, 'ignore') + unbind_keys(o.list_move_up_keybind, 'move-up') + unbind_keys(o.list_move_down_keybind, 'move-down') + unbind_keys(o.list_move_first_keybind, 'move-first') + unbind_keys(o.list_move_last_keybind, 'move-last') + unbind_keys(o.list_page_up_keybind, 'page-up') + unbind_keys(o.list_page_down_keybind, 'page-down') + unbind_keys(o.list_select_keybind, 'list-select') + unbind_keys(o.list_add_playlist_keybind, 'list-add-playlist') + unbind_keys(o.list_add_playlist_highlighted_keybind, 'list-add-playlist-highlight') + unbind_keys(o.list_delete_keybind, 'list-delete') + unbind_keys(o.list_delete_highlighted_keybind, 'list-delete-highlight') + unbind_keys(o.list_close_keybind, 'list-close') + unbind_keys(o.next_filter_sequence_keybind, 'list-filter-next') + unbind_keys(o.previous_filter_sequence_keybind, 'list-filter-previous') + unbind_keys(o.list_highlight_all_keybind, 'list-highlight-all') + unbind_keys(o.list_highlight_all_keybind, 'list-unhighlight-all') + unbind_keys(o.list_cycle_sort_keybind, 'list-cycle-sort') + + for i = 1, #o.list_move_up_keybind do + mp.remove_key_binding('highlight-move-up'..i) + end + for i = 1, #o.list_move_down_keybind do + mp.remove_key_binding('highlight-move-down'..i) + end + for i = 1, #o.list_move_first_keybind do + mp.remove_key_binding('highlight-move-first'..i) + end + for i = 1, #o.list_move_last_keybind do + mp.remove_key_binding('highlight-move-last'..i) + end + for i = 1, #o.list_page_up_keybind do + mp.remove_key_binding('highlight-page-up'..i) + end + for i = 1, #o.list_page_down_keybind do + mp.remove_key_binding('highlight-page-down'..i) + end + + for i = 1, #o.list_filter_jump_keybind do + mp.remove_key_binding('list-filter-jump'..i) + end + + for i = 1, #o.open_list_keybind do + if i == 1 then + mp.add_forced_key_binding(o.open_list_keybind[i][1], 'open-list', function()display_list(o.open_list_keybind[i][2]) end) + else + mp.add_forced_key_binding(o.open_list_keybind[i][1], 'open-list'..i, function()display_list(o.open_list_keybind[i][2]) end) + end + end + + if o.quickselect_0to9_keybind and o.list_show_amount <= 10 then + mp.remove_key_binding("recent-1") + mp.remove_key_binding("recent-2") + mp.remove_key_binding("recent-3") + mp.remove_key_binding("recent-4") + mp.remove_key_binding("recent-5") + mp.remove_key_binding("recent-6") + mp.remove_key_binding("recent-7") + mp.remove_key_binding("recent-8") + mp.remove_key_binding("recent-9") + mp.remove_key_binding("recent-0") + end +end + +function list_close_and_trash_collection() + if utils.shared_script_property_set then + utils.shared_script_property_set('simplehistory-menu-open', 'no') + end + mp.set_property('user-data/simplehistory/menu-open', 'no') + if o.toggle_idlescreen then mp.commandv('script-message', 'osc-idlescreen', 'yes', 'no_osd') end + unbind_list_keys() + unbind_search_keys() + mp.set_osd_ass(0, 0, "") + list_drawn = false + list_cursor = 1 + list_start = 0 + filterName = 'all' + list_pages = {} + search_string = '' + search_active = false + list_highlight_cursor = {} + sortName = nil +end +--End of LogManager (List Bind and Unbind)-- + +--LogManager Search Feature-- +function list_search_exit() + search_active = false + get_list_contents(filterName) + get_page_properties(filterName) + select(0) + unbind_search_keys() + get_list_keybinds() +end + +function list_search_not_typing_mode(auto_triggered) + if auto_triggered then + if search_string ~= '' and list_contents[1] then + search_active = 'not_typing' + elseif not list_contents[1] then + return + else + search_active = false + end + else + if search_string ~= '' then + search_active = 'not_typing' + else + search_active = false + end + end + select(0) + unbind_search_keys() + get_list_keybinds() +end + +function list_search_activate() + if not list_drawn then return end + if search_active == 'typing' then list_search_exit() return end + search_active = 'typing' + + for i = 1, #list_pages do + if list_pages[i][1] == filterName then + list_pages[i][2] = list_cursor + list_pages[i][4] = list_highlight_cursor + list_pages[i][5] = sortName + end + end + + update_search_results('','') + bind_search_keys() +end + +function update_search_results(character, action) + if not character then character = '' end + if action == 'string_del' then + search_string = search_string:sub(1, -2) + end + search_string = search_string..character + local prev_contents_length = #list_contents + get_list_contents(filterName) + + if prev_contents_length ~= #list_contents then + list_highlight_cursor = {} + end + + if character ~= '' and #list_contents > 0 or action ~= nil and #list_contents > 0 then + select(1-list_cursor) + elseif #list_contents == 0 then + list_cursor = 0 + select(list_cursor) + else + select(0) + end +end + +function bind_search_keys() + mp.add_forced_key_binding('a', 'search_string_a', function() update_search_results('a') end, 'repeatable') + mp.add_forced_key_binding('b', 'search_string_b', function() update_search_results('b') end, 'repeatable') + mp.add_forced_key_binding('c', 'search_string_c', function() update_search_results('c') end, 'repeatable') + mp.add_forced_key_binding('d', 'search_string_d', function() update_search_results('d') end, 'repeatable') + mp.add_forced_key_binding('e', 'search_string_e', function() update_search_results('e') end, 'repeatable') + mp.add_forced_key_binding('f', 'search_string_f', function() update_search_results('f') end, 'repeatable') + mp.add_forced_key_binding('g', 'search_string_g', function() update_search_results('g') end, 'repeatable') + mp.add_forced_key_binding('h', 'search_string_h', function() update_search_results('h') end, 'repeatable') + mp.add_forced_key_binding('i', 'search_string_i', function() update_search_results('i') end, 'repeatable') + mp.add_forced_key_binding('j', 'search_string_j', function() update_search_results('j') end, 'repeatable') + mp.add_forced_key_binding('k', 'search_string_k', function() update_search_results('k') end, 'repeatable') + mp.add_forced_key_binding('l', 'search_string_l', function() update_search_results('l') end, 'repeatable') + mp.add_forced_key_binding('m', 'search_string_m', function() update_search_results('m') end, 'repeatable') + mp.add_forced_key_binding('n', 'search_string_n', function() update_search_results('n') end, 'repeatable') + mp.add_forced_key_binding('o', 'search_string_o', function() update_search_results('o') end, 'repeatable') + mp.add_forced_key_binding('p', 'search_string_p', function() update_search_results('p') end, 'repeatable') + mp.add_forced_key_binding('q', 'search_string_q', function() update_search_results('q') end, 'repeatable') + mp.add_forced_key_binding('r', 'search_string_r', function() update_search_results('r') end, 'repeatable') + mp.add_forced_key_binding('s', 'search_string_s', function() update_search_results('s') end, 'repeatable') + mp.add_forced_key_binding('t', 'search_string_t', function() update_search_results('t') end, 'repeatable') + mp.add_forced_key_binding('u', 'search_string_u', function() update_search_results('u') end, 'repeatable') + mp.add_forced_key_binding('v', 'search_string_v', function() update_search_results('v') end, 'repeatable') + mp.add_forced_key_binding('w', 'search_string_w', function() update_search_results('w') end, 'repeatable') + mp.add_forced_key_binding('x', 'search_string_x', function() update_search_results('x') end, 'repeatable') + mp.add_forced_key_binding('y', 'search_string_y', function() update_search_results('y') end, 'repeatable') + mp.add_forced_key_binding('z', 'search_string_z', function() update_search_results('z') end, 'repeatable') + + mp.add_forced_key_binding('A', 'search_string_A', function() update_search_results('A') end, 'repeatable') + mp.add_forced_key_binding('B', 'search_string_B', function() update_search_results('B') end, 'repeatable') + mp.add_forced_key_binding('C', 'search_string_C', function() update_search_results('C') end, 'repeatable') + mp.add_forced_key_binding('D', 'search_string_D', function() update_search_results('D') end, 'repeatable') + mp.add_forced_key_binding('E', 'search_string_E', function() update_search_results('E') end, 'repeatable') + mp.add_forced_key_binding('F', 'search_string_F', function() update_search_results('F') end, 'repeatable') + mp.add_forced_key_binding('G', 'search_string_G', function() update_search_results('G') end, 'repeatable') + mp.add_forced_key_binding('H', 'search_string_H', function() update_search_results('H') end, 'repeatable') + mp.add_forced_key_binding('I', 'search_string_I', function() update_search_results('I') end, 'repeatable') + mp.add_forced_key_binding('J', 'search_string_J', function() update_search_results('J') end, 'repeatable') + mp.add_forced_key_binding('K', 'search_string_K', function() update_search_results('K') end, 'repeatable') + mp.add_forced_key_binding('L', 'search_string_L', function() update_search_results('L') end, 'repeatable') + mp.add_forced_key_binding('M', 'search_string_M', function() update_search_results('M') end, 'repeatable') + mp.add_forced_key_binding('N', 'search_string_N', function() update_search_results('N') end, 'repeatable') + mp.add_forced_key_binding('O', 'search_string_O', function() update_search_results('O') end, 'repeatable') + mp.add_forced_key_binding('P', 'search_string_P', function() update_search_results('P') end, 'repeatable') + mp.add_forced_key_binding('Q', 'search_string_Q', function() update_search_results('Q') end, 'repeatable') + mp.add_forced_key_binding('R', 'search_string_R', function() update_search_results('R') end, 'repeatable') + mp.add_forced_key_binding('S', 'search_string_S', function() update_search_results('S') end, 'repeatable') + mp.add_forced_key_binding('T', 'search_string_T', function() update_search_results('T') end, 'repeatable') + mp.add_forced_key_binding('U', 'search_string_U', function() update_search_results('U') end, 'repeatable') + mp.add_forced_key_binding('V', 'search_string_V', function() update_search_results('V') end, 'repeatable') + mp.add_forced_key_binding('W', 'search_string_W', function() update_search_results('W') end, 'repeatable') + mp.add_forced_key_binding('X', 'search_string_X', function() update_search_results('X') end, 'repeatable') + mp.add_forced_key_binding('Y', 'search_string_Y', function() update_search_results('Y') end, 'repeatable') + mp.add_forced_key_binding('Z', 'search_string_Z', function() update_search_results('Z') end, 'repeatable') + + mp.add_forced_key_binding('1', 'search_string_1', function() update_search_results('1') end, 'repeatable') + mp.add_forced_key_binding('2', 'search_string_2', function() update_search_results('2') end, 'repeatable') + mp.add_forced_key_binding('3', 'search_string_3', function() update_search_results('3') end, 'repeatable') + mp.add_forced_key_binding('4', 'search_string_4', function() update_search_results('4') end, 'repeatable') + mp.add_forced_key_binding('5', 'search_string_5', function() update_search_results('5') end, 'repeatable') + mp.add_forced_key_binding('6', 'search_string_6', function() update_search_results('6') end, 'repeatable') + mp.add_forced_key_binding('7', 'search_string_7', function() update_search_results('7') end, 'repeatable') + mp.add_forced_key_binding('8', 'search_string_8', function() update_search_results('8') end, 'repeatable') + mp.add_forced_key_binding('9', 'search_string_9', function() update_search_results('9') end, 'repeatable') + mp.add_forced_key_binding('0', 'search_string_0', function() update_search_results('0') end, 'repeatable') + + mp.add_forced_key_binding('SPACE', 'search_string_space', function() update_search_results(' ') end, 'repeatable') + mp.add_forced_key_binding('`', 'search_string_`', function() update_search_results('`') end, 'repeatable') + mp.add_forced_key_binding('~', 'search_string_~', function() update_search_results('~') end, 'repeatable') + mp.add_forced_key_binding('!', 'search_string_!', function() update_search_results('!') end, 'repeatable') + mp.add_forced_key_binding('@', 'search_string_@', function() update_search_results('@') end, 'repeatable') + mp.add_forced_key_binding('SHARP', 'search_string_sharp', function() update_search_results('#') end, 'repeatable') + mp.add_forced_key_binding('$', 'search_string_$', function() update_search_results('$') end, 'repeatable') + mp.add_forced_key_binding('%', 'search_string_percentage', function() update_search_results('%') end, 'repeatable') + mp.add_forced_key_binding('^', 'search_string_^', function() update_search_results('^') end, 'repeatable') + mp.add_forced_key_binding('&', 'search_string_&', function() update_search_results('&') end, 'repeatable') + mp.add_forced_key_binding('*', 'search_string_*', function() update_search_results('*') end, 'repeatable') + mp.add_forced_key_binding('(', 'search_string_(', function() update_search_results('(') end, 'repeatable') + mp.add_forced_key_binding(')', 'search_string_)', function() update_search_results(')') end, 'repeatable') + mp.add_forced_key_binding('-', 'search_string_-', function() update_search_results('-') end, 'repeatable') + mp.add_forced_key_binding('_', 'search_string__', function() update_search_results('_') end, 'repeatable') + mp.add_forced_key_binding('=', 'search_string_=', function() update_search_results('=') end, 'repeatable') + mp.add_forced_key_binding('+', 'search_string_+', function() update_search_results('+') end, 'repeatable') + mp.add_forced_key_binding('\\', 'search_string_\\', function() update_search_results('\\') end, 'repeatable') + mp.add_forced_key_binding('|', 'search_string_|', function() update_search_results('|') end, 'repeatable') + mp.add_forced_key_binding(']', 'search_string_]', function() update_search_results(']') end, 'repeatable') + mp.add_forced_key_binding('}', 'search_string_rightcurly', function() update_search_results('}') end, 'repeatable') + mp.add_forced_key_binding('[', 'search_string_[', function() update_search_results('[') end, 'repeatable') + mp.add_forced_key_binding('{', 'search_string_leftcurly', function() update_search_results('{') end, 'repeatable') + mp.add_forced_key_binding('\'', 'search_string_\'', function() update_search_results('\'') end, 'repeatable') + mp.add_forced_key_binding('\"', 'search_string_\"', function() update_search_results('\"') end, 'repeatable') + mp.add_forced_key_binding(';', 'search_string_semicolon', function() update_search_results(';') end, 'repeatable') + mp.add_forced_key_binding(':', 'search_string_:', function() update_search_results(':') end, 'repeatable') + mp.add_forced_key_binding('/', 'search_string_/', function() update_search_results('/') end, 'repeatable') + mp.add_forced_key_binding('?', 'search_string_?', function() update_search_results('?') end, 'repeatable') + mp.add_forced_key_binding('.', 'search_string_.', function() update_search_results('.') end, 'repeatable') + mp.add_forced_key_binding('>', 'search_string_>', function() update_search_results('>') end, 'repeatable') + mp.add_forced_key_binding(',', 'search_string_,', function() update_search_results(',') end, 'repeatable') + mp.add_forced_key_binding('<', 'search_string_<', function() update_search_results('<') end, 'repeatable') + + mp.add_forced_key_binding('bs', 'search_string_del', function() update_search_results('', 'string_del') end, 'repeatable') + bind_keys(o.list_close_keybind, 'search_exit', function() list_search_exit() end) + bind_keys(o.list_search_not_typing_mode_keybind, 'search_string_not_typing', function()list_search_not_typing_mode(false) end) + + if o.search_not_typing_smartly then + bind_keys(o.next_filter_sequence_keybind, 'list-filter-next', function() list_filter_next() list_search_not_typing_mode(true) end) + bind_keys(o.previous_filter_sequence_keybind, 'list-filter-previous', function() list_filter_previous() list_search_not_typing_mode(true) end) + bind_keys(o.list_delete_keybind, 'list-delete', function() list_delete() list_search_not_typing_mode(true) end) + bind_keys(o.list_delete_highlighted_keybind, 'list-delete-highlight', function() list_delete('highlight') list_search_not_typing_mode(true) end) + end +end + +function unbind_search_keys() + mp.remove_key_binding('search_string_a') + mp.remove_key_binding('search_string_b') + mp.remove_key_binding('search_string_c') + mp.remove_key_binding('search_string_d') + mp.remove_key_binding('search_string_e') + mp.remove_key_binding('search_string_f') + mp.remove_key_binding('search_string_g') + mp.remove_key_binding('search_string_h') + mp.remove_key_binding('search_string_i') + mp.remove_key_binding('search_string_j') + mp.remove_key_binding('search_string_k') + mp.remove_key_binding('search_string_l') + mp.remove_key_binding('search_string_m') + mp.remove_key_binding('search_string_n') + mp.remove_key_binding('search_string_o') + mp.remove_key_binding('search_string_p') + mp.remove_key_binding('search_string_q') + mp.remove_key_binding('search_string_r') + mp.remove_key_binding('search_string_s') + mp.remove_key_binding('search_string_t') + mp.remove_key_binding('search_string_u') + mp.remove_key_binding('search_string_v') + mp.remove_key_binding('search_string_w') + mp.remove_key_binding('search_string_x') + mp.remove_key_binding('search_string_y') + mp.remove_key_binding('search_string_z') + + mp.remove_key_binding('search_string_A') + mp.remove_key_binding('search_string_B') + mp.remove_key_binding('search_string_C') + mp.remove_key_binding('search_string_D') + mp.remove_key_binding('search_string_E') + mp.remove_key_binding('search_string_F') + mp.remove_key_binding('search_string_G') + mp.remove_key_binding('search_string_H') + mp.remove_key_binding('search_string_I') + mp.remove_key_binding('search_string_J') + mp.remove_key_binding('search_string_K') + mp.remove_key_binding('search_string_L') + mp.remove_key_binding('search_string_M') + mp.remove_key_binding('search_string_N') + mp.remove_key_binding('search_string_O') + mp.remove_key_binding('search_string_P') + mp.remove_key_binding('search_string_Q') + mp.remove_key_binding('search_string_R') + mp.remove_key_binding('search_string_S') + mp.remove_key_binding('search_string_T') + mp.remove_key_binding('search_string_U') + mp.remove_key_binding('search_string_V') + mp.remove_key_binding('search_string_W') + mp.remove_key_binding('search_string_X') + mp.remove_key_binding('search_string_Y') + mp.remove_key_binding('search_string_Z') + + mp.remove_key_binding('search_string_1') + mp.remove_key_binding('search_string_2') + mp.remove_key_binding('search_string_3') + mp.remove_key_binding('search_string_4') + mp.remove_key_binding('search_string_5') + mp.remove_key_binding('search_string_6') + mp.remove_key_binding('search_string_7') + mp.remove_key_binding('search_string_8') + mp.remove_key_binding('search_string_9') + mp.remove_key_binding('search_string_0') + + mp.remove_key_binding('search_string_space') + mp.remove_key_binding('search_string_`') + mp.remove_key_binding('search_string_~') + mp.remove_key_binding('search_string_!') + mp.remove_key_binding('search_string_@') + mp.remove_key_binding('search_string_sharp') + mp.remove_key_binding('search_string_$') + mp.remove_key_binding('search_string_percentage') + mp.remove_key_binding('search_string_^') + mp.remove_key_binding('search_string_&') + mp.remove_key_binding('search_string_*') + mp.remove_key_binding('search_string_(') + mp.remove_key_binding('search_string_)') + mp.remove_key_binding('search_string_-') + mp.remove_key_binding('search_string__') + mp.remove_key_binding('search_string_=') + mp.remove_key_binding('search_string_+') + mp.remove_key_binding('search_string_\\') + mp.remove_key_binding('search_string_|') + mp.remove_key_binding('search_string_]') + mp.remove_key_binding('search_string_rightcurly') + mp.remove_key_binding('search_string_[') + mp.remove_key_binding('search_string_leftcurly') + mp.remove_key_binding('search_string_\'') + mp.remove_key_binding('search_string_\"') + mp.remove_key_binding('search_string_semicolon') + mp.remove_key_binding('search_string_:') + mp.remove_key_binding('search_string_/') + mp.remove_key_binding('search_string_?') + mp.remove_key_binding('search_string_.') + mp.remove_key_binding('search_string_>') + mp.remove_key_binding('search_string_,') + mp.remove_key_binding('search_string_<') + + mp.remove_key_binding('search_string_del') + if not search_active then + unbind_keys(o.list_close_keybind, 'search_exit') + end +end +--End of LogManager Search Feature-- +---------End of LogManager--------- + +function history_blacklist_check() + if not o.history_blacklist[1] or #o.history_blacklist == 1 and o.history_blacklist[1] == "" then return false end + local invertable_return = {true, false} + local blacklist_msg = 'File was not added to history because of blacklist' + if o.invert_history_blacklist then + invertable_return = {false, true} + blacklist_msg = 'File was added to history because of whitelist' + end + + if has_value(o.history_blacklist, filePath, nil) then + msg.info(blacklist_msg) + return invertable_return[1] + elseif not starts_protocol(protocols, filePath) then + if has_value(o.history_blacklist, filePath:match('^(.-)([^\\/]-)%.([^\\/%.]-)%.?$'), nil) or + has_value(o.history_blacklist, filePath:match('^(.-)([^\\/]-)%.([^\\/%.]-)%.?$'):gsub('\\$', ''), nil) then + msg.info(blacklist_msg) + return invertable_return[1] + elseif has_value(o.history_blacklist, filePath:match('%.([^%.]+)$'), nil) or + has_value(o.history_blacklist, "."..filePath:match('%.([^%.]+)$'), nil) then + msg.info(blacklist_msg) + return invertable_return[1] + else --1.1.2# check to add any subfolder after /* to blacklist. issue #70 + for i=1, #o.history_blacklist do --1.1.2# loop through blacklisted items, if the blacklist ends with * and it is a match after subbing of the current filePath then log it. #and additionally if it is the exact same path then ignore it. + if string.lower(filePath):match(string.lower(o.history_blacklist[i])) and o.history_blacklist[i]:sub(-1,#o.history_blacklist[i]) == '*' and string.lower(o.history_blacklist[i]:sub(1,-2)) ~= string.lower(filePath):match("(.*[\\/])") then + msg.info(blacklist_msg) + return invertable_return[1] + end + end + end + elseif starts_protocol(protocols, filePath) then + if has_value(o.history_blacklist, filePath:match('(.-)(:)'), nil) or + has_value(o.history_blacklist, filePath:match('(.-:)'), nil) or + has_value(o.history_blacklist, filePath:match('(.-:/?/?)'), nil) then + msg.info(blacklist_msg) + return invertable_return[1] + elseif filePath:find('https?://') == 1 then + local difchk_1, difchk_2 = filePath:match("(https?://)w?w?w?%.?([%w%.%:]*)") + local different_check_temp = difchk_1..difchk_2 + local different_checks = {different_check_temp, filePath:match("https?://w?w?w?%.?([%w%.%:]*)"), filePath:match("https?://([%w%.%:]*)"), filePath:match("(https?://[%w%.%:]*)") } + for i = 1, #different_checks do + if different_checks[i] and has_value(o.history_blacklist, different_checks[i], nil) + or different_checks[i]..'/' and has_value(o.history_blacklist, different_checks[i]..'/', nil) then + msg.info(blacklist_msg) + return invertable_return[1] + end + end + end + end + + return invertable_return[2] +end + +function mark_chapter() + if not o.mark_history_as_chapter then return end + + local all_chapters = mp.get_property_native("chapter-list") + local chapter_index = 0 + local chapters_time = {} + + get_list_contents() + if not list_contents or not list_contents[1] then return end + for i = 1, #list_contents do + if list_contents[i].found_path == filePath and tonumber(list_contents[i].found_time) > 0 then + table.insert(chapters_time, tonumber(list_contents[i].found_time)) + end + end + if not chapters_time[1] then return end + + table.sort(chapters_time, function(a, b) return a < b end) + + for i = 1, #chapters_time do + chapter_index = chapter_index + 1 + + all_chapters[chapter_index] = { + title = 'SimpleHistory ' .. chapter_index, + time = chapters_time[i] + } + end + + table.sort(all_chapters, function(a, b) return a['time'] < b['time'] end) + + mp.set_property_native("chapter-list", all_chapters) +end + +function write_log(target_time, update_seekTime, entry_limit) + if not filePath then return end + local prev_seekTime = seekTime + seekTime = (mp.get_property_number('time-pos') or 0) + if target_time then + seekTime = target_time + end + if seekTime < 0 then seekTime = 0 end + + delete_log_entry(false, true, filePath, math.floor(seekTime), entry_limit) + + local f = io.open(log_fullpath, "a+") + if o.file_title_logging == 'all' then + f:write(("[%s] \"%s\" | %s | %s | %s"):format(os.date(o.date_format), fileTitle, filePath, log_length_text .. tostring(fileLength), log_time_text .. tostring(seekTime))) + elseif o.file_title_logging == 'protocols' and (starts_protocol(o.logging_protocols, filePath)) then + f:write(("[%s] \"%s\" | %s | %s | %s"):format(os.date(o.date_format), fileTitle, filePath, log_length_text .. tostring(fileLength), log_time_text .. tostring(seekTime))) + elseif o.file_title_logging == 'protocols' and not (starts_protocol(o.logging_protocols, filePath)) then + f:write(("[%s] %s | %s | %s"):format(os.date(o.date_format), filePath, log_length_text .. tostring(fileLength), log_time_text .. tostring(seekTime))) + else + f:write(("[%s] %s | %s | %s"):format(os.date(o.date_format), filePath, log_length_text .. tostring(fileLength), log_time_text .. tostring(seekTime))) + end + + f:write('\n') + f:close() + + if not update_seekTime then + seekTime = prev_seekTime + end +end + +function history_incognito_mode() + if not incognito_mode then + incognito_mode = true + if o.osd_messages == true then + mp.osd_message('🕵 Incognito Mode Enabled') + end + msg.info('Incognito Mode Enabled') + + if o.delete_incognito_entry and autosaved_entry == true then + delete_log_entry_specific('last', filePath, 0) + autosaved_entry = 'autosaved-restore' + if list_drawn then + get_list_contents() + select(0) + end + end + else + incognito_mode = false + if o.osd_messages == true then + mp.osd_message('Incognito Mode Disabled') + end + msg.info('Incognito Mode Disabled') + + if o.restore_incognito_entry == 'always' then + history_fileonly_save() + autosaved_entry = true + elseif o.restore_incognito_entry == 'deleted-restore' and autosaved_entry == 'autosaved-restore' then + history_fileonly_save() + autosaved_entry = true + if list_drawn then + get_list_contents() + select(0) + end + end + end +end + +function history_resume_option() + if o.resume_option == 'notification' or o.resume_option == 'force' then + local video_time = mp.get_property_number('time-pos') + local video_path = mp.get_property('path') --1.1.4# local variable instead of filePath + if video_time > 0 then return end + local logged_time = 0 + local percentage = 0 + local video_duration = (mp.get_property_number('duration') or 0) + list_contents = read_log_table() + if not list_contents or not list_contents[1] then return end + for i = #list_contents, 1, -1 do + if list_contents[i].found_path == video_path and tonumber(list_contents[i].found_time) > 0 then --1.1.4# instead of filePath in case it is causing issue + logged_time = tonumber(list_contents[i].found_time) + o.resume_offset + break + end + end + if logged_time > 0 then + percentage = math.floor((logged_time / video_duration) * 100 + 0.5) + if o.resume_option == 'notification' then + if percentage > o.resume_option_threshold and percentage < (100-o.resume_option_threshold) or o.resume_option_threshold == 0 then + mp.osd_message('⌨ [' .. string.upper(o.history_resume_keybind[1]) .. '] Resumes To' .. o.time_seperator .. format_time(logged_time, o.osd_time_format[3], o.osd_time_format[2], o.osd_time_format[1]),3) + end + elseif o.resume_option == 'force' then + if percentage > o.resume_option_threshold and percentage < (100-o.resume_option_threshold) or o.resume_option_threshold == 0 then + mp.commandv('seek', logged_time, 'absolute', 'exact') + if (o.osd_messages == true) then + mp.osd_message('Resumed To Last Played Position\n' .. o.time_seperator .. format_time(logged_time, o.osd_time_format[3], o.osd_time_format[2], o.osd_time_format[1])) + end + msg.info('Resumed to the last played position') + end + end + end + end +end + +function history_save(target_time) + if filePath ~= nil then + if history_blacklist_check() then + return + end + write_log(target_time, false, o.same_entry_limit) + if list_drawn then + get_list_contents() + select(0) + end + msg.info('Added the below into history\n' .. fileTitle .. o.time_seperator .. format_time(seekTime)) + else + msg.info("Failed to add into history") + end +end + +function history_fileonly_save() + if filePath ~= nil then + if history_blacklist_check() then + return + end + write_log(0, false) + if list_drawn then + get_list_contents() + select(0) + end + msg.info('Added the below into history\n' .. fileTitle .. o.time_seperator .. format_time(seekTime)) + else + msg.info("Failed to add into history, no file found") + end +end + +function history_resume() + if filePath == nil then + list_contents = read_log_table() + load(1) + elseif filePath ~= nil then + list_contents = read_log_table() + if list_contents ~= nil and list_contents[1] then + for i = #list_contents, 1, -1 do + if list_contents[i].found_path == filePath and tonumber(list_contents[i].found_time) > 0 then + seekTime = tonumber(list_contents[i].found_time) + o.resume_offset + break + end + end + end + if seekTime > 0 then + mp.commandv('seek', seekTime, 'absolute', 'exact') + if (o.osd_messages == true) then + mp.osd_message('Resumed To Last Played Position\n' .. o.time_seperator .. format_time(seekTime, o.osd_time_format[3], o.osd_time_format[2], o.osd_time_format[1])) + end + msg.info('Resumed to the last played position') + else + if (o.osd_messages == true) then + mp.osd_message('No Resume Position Found For This Video') + end + msg.info('No resume position found for this video') + end + end +end + +function history_load_last() + if filePath == nil then + list_contents = read_log_table() + load(1, false, 0) + elseif filePath ~= nil then + list_contents = read_log_table() + load(2, true) + end +end + +mp.register_event('file-loaded', function() + list_close_and_trash_collection() + filePath, fileTitle, fileLength = get_file() + loadTriggered = true --1.1.5# for resume and resume-notime startup behavior (so that it only triggers if started as idle and only once) + if (resume_selected == true and seekTime > 0) then + mp.commandv('seek', seekTime, 'absolute', 'exact') + resume_selected = false + end + history_resume_option() --1.1.4# remove timeout, cant remember why I put it in first place + mark_chapter() + if not incognito_mode then + history_fileonly_save() + autosaved_entry = true + end +end) + +mp.add_hook('on_unload', 9, function()--1.1.3# get the LogTime only when using on_unload because big functions do not run fully in here + logTime = (mp.get_property_number('time-pos') or 0) +end) +mp.register_event('end-file', function()--1.1.3# use end-file instead so that it doesn't cause crash while seeking ( i am able to run big functions here) + if not incognito_mode then + if autosaved_entry == true then delete_log_entry_specific('last', filePath, 0) end + history_save(logTime) --1.1.3# get the updated time from on_unload since it will still be preserved + end + autosaved_entry = false + logTime = 0 --1.1.3# reset logTime to 0 +end) + +mp.observe_property("idle-active", "bool", function(_, v) + if v then --1.1.2# if idle is triggered + filePath, fileTitle, fileLength = nil --1.1.2# set it back to nil if idle is triggered for better trash collection. issue #69 + end + + if v and o.startup_idle_behavior == 'resume' and not loadTriggered then --1.1.5# option to resume on startup + history_resume() + elseif v and o.startup_idle_behavior == 'resume-notime' and not loadTriggered then --1.1.5# option to load last item on startup + history_load_last() + elseif v and has_value(available_filters, o.auto_run_list_idle) then + display_list(o.auto_run_list_idle, nil, 'hide-osd') + end + + if v and o.auto_run_incognito_mode and not incognito_auto_run_triggered or + not v and o.auto_run_incognito_mode and not incognito_auto_run_triggered then + history_incognito_mode() + incognito_auto_run_triggered = true + end +end) + +bind_keys(o.history_resume_keybind, 'history-resume', history_resume) +bind_keys(o.history_load_last_keybind, 'history-load-last', history_load_last) +bind_keys(o.history_incognito_mode_keybind, 'history-incognito-mode', history_incognito_mode) + +for i = 1, #o.open_list_keybind do + if i == 1 then + mp.add_forced_key_binding(o.open_list_keybind[i][1], 'open-list', function()display_list(o.open_list_keybind[i][2]) end) + else + mp.add_forced_key_binding(o.open_list_keybind[i][1], 'open-list'..i, function()display_list(o.open_list_keybind[i][2]) end) + end +end