-
Notifications
You must be signed in to change notification settings - Fork 49
Expand file tree
/
Copy pathSimpleBookmark.lua
More file actions
2907 lines (2581 loc) · 133 KB
/
SimpleBookmark.lua
File metadata and controls
2907 lines (2581 loc) · 133 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
-- Copyright (c) 2023, Eisa AlAwadhi
-- License: BSD 2-Clause License
-- Creator: Eisa AlAwadhi
-- Project: SimpleBookmark
-- Version: 1.3.1
local o = {
---------------------------USER CUSTOMIZATION SETTINGS---------------------------
--These settings are for users to manually change some options.
--Changes are recommended to be made in the script-opts directory.
-----Script Settings----
--Available filters: 'all', 'keybinds', 'groups', 'recents', 'distinct', 'protocols', 'fileonly', 'titleonly', 'timeonly', 'keywords'.
--Filters description: "all" to display all the items. Or 'groups' to display the list filtered with items added to any group. Or 'keybinds' to display the list filtered with keybind slots. Or "recents" to display recently added items to log without duplicate. Or "distinct" to show recent saved entries for files in different paths. Or "fileonly" to display files saved without time. Or "timeonly" to display files that have time only. Or "keywords" to display files with matching keywords specified in the configuration. Or "playing" to show list of current playing file.
--Filters can also be stacked by using %+% or omitted by using %-%. e.g.: "groups%+%keybinds" shows only groups and keybinds, "all%-%groups%-%keybinds" shows all items without groups and without keybinds.
--Also defined groups can be called by using /:group%Group Name%
auto_run_list_idle = 'none', --Auto run the list when opening mpv and there is no video / file loaded. none for disabled. Or choose between available filters.
load_item_on_startup = 0, --runs a saved entry when mpv starts based on its number. -1 for oldest entry, 1 for latest entry, or select the number to load a specific entry, 0 for disabled
toggle_idlescreen = true, --hides OSC idle screen message when opening and closing menu (could cause unexpected behavior if multiple scripts are triggering osc-idlescreen off)
resume_offset = -0.65, --change to 0 so item resumes from the exact position, or decrease the value so that it gives you a little preview before loading the resume point
osd_messages = true, --true is for displaying osd messages when actions occur. Change to false will disable all osd messages generated from this script
bookmark_loads_last_idle = true, --When attempting to bookmark, if there is no video / file loaded, it will instead jump to your last bookmarked item and resume it.
bookmark_fileonly_loads_last_idle = true, --When attempting to bookmark fileonly, if there is no video / file loaded, it will instead jump to your last bookmarked item without resuming.
mark_bookmark_as_chapter = false, --true is for marking the time as a chapter. false disables mark as chapter behavior.
preserve_video_settings = false, --(true/false). Preserve video settings when bookmarking items and loading bookmarks by writing mpv watch-later config
bookmark_save_keybind=[[
["ctrl+b", "ctrl+B"]
]], --Keybind that will be used to save the video and its time to log file
bookmark_fileonly_keybind=[[
["alt+b", "alt+B"]
]], --Keybind that will be used to save the video without time to log file
open_list_keybind=[[
[ ["b", "all"], ["B", "all"], ["k", "keybinds"], ["K", "keybinds"] ]
]], --Keybind that will be used to open the list along with the specified filter.
list_filter_jump_keybind=[[
[ ["b", "all"], ["B", "all"], ["k", "keybinds"], ["K", "keybinds"], ["!", "/:group%TV Shows%"], ["@", "/:group%Movies%"], ["SHARP", "/:group%Anime%"], ["$", "/:group%Anime Movies%"], ["%", "/:group%Cartoon%"], ["r", "recents"], ["R", "recents"], ["d", "distinct"], ["D", "distinct"], ["f", "fileonly"], ["F", "fileonly"] ]
]], --Keybind that is used while the list is open to jump to the specific filter (it also enables pressing a filter keybind twice to close list). Available fitlers: 'all', 'keybinds', 'recents', 'distinct', 'protocols', 'fileonly', 'titleonly', 'timeonly', 'keywords'.
-----Keybind Slots Settings-----
keybinds_quicksave_fileonly = true, --When quick saving to a keybind slot, it will not save position
keybinds_empty_auto_create = false, --If the keybind slot is empty, this enables quick logging and adding to slot, Otherwise keybinds are assigned from the list or via quicksave.
keybinds_empty_fileonly = true, --When auto creating keybind slot, it will not save position.
keybinds_auto_resume = true, --When loading a keybind slot, it will auto resume to the saved time.
keybinds_add_load_keybind=[[
["alt+1", "alt+2", "alt+3", "alt+4", "alt+5", "alt+6", "alt+7", "alt+8", "alt+9"]
]], --Keybind that will be used to bind list item to a key, as well as to load it. e.g.: Press alt+1 on list cursor position to add it, press alt+1 while list is hidden to load item keybinded into alt+1. (A new slot is automatically created for each keybind. e.g: .."alt+9, alt+0". Where alt+0 creates a new 10th slot.)
keybinds_quicksave_keybind=[[
["alt+!", "alt+@", "alt+#", "alt+$", "alt+%", "alt+^", "alt+&", "alt+*", "alt+("]
]], --To save keybind to a slot without opening the list, to load these keybinds it uses keybinds_add_load_keybind
keybinds_remove_keybind=[[
["alt+-"]
]], --Keybind that is used when list is open to remove the keybind slot based on cursor position
keybinds_remove_highlighted_keybind=[[
["alt+_"]
]], --Keybind that is used when list is open to remove the keybind slot based on highlighted items
-----Group Settings-----
groups_list_and_keybind =[[
[ ["TV Shows", "ctrl+1", "ctrl+!"], ["Movies", "ctrl+2", "ctrl+@"], ["Anime", "ctrl+3", "ctrl+#"], ["Anime Movies", "ctrl+4", "ctrl+$"], ["Cartoon", "ctrl+5"], ["Animated Movies"] ]
]], --Define the groups that can be assigned to a bookmarked item, you can also optionally assign the keybind, and the highlight keybind that puts the bookmarked item into the relevant group when the list is open. Alternatively you can use list_group_add_cycle_keybind to assign item to a group
list_groups_remove_keybind=[[
["ctrl+-"]
]], --Keybind that is used when list is open to remove the group based on cursor position
list_groups_remove_highlighted_keybind=[[
["ctrl+_"]
]], --Keybind that is used when list is open to remove the group based on highlighted items
list_group_add_cycle_keybind=[[
["ctrl+g"]
]], --Keybind to add an item to the group, this cycles through all the different available groups when list is open
list_group_add_cycle_highlighted_keybind=[[
["ctrl+G"]
]], --Keybind to add highlighted items to the group, this cycles through all the different available groups when list is open
-----Logging Settings-----
log_path = '/:dir%mpvconf%', --Change to '/:dir%script%' for placing it in the same directory of script, OR change to '/:dir%mpvconf%' for mpv portable_config directory. OR write any variable using '/:var' then the variable '/:var%APPDATA%' you can use path also, such as: '/:var%APPDATA%\\mpv' OR '/:var%HOME%/mpv' OR specify the absolute path , e.g.: 'C:\\Users\\Eisa01\\Desktop\\'
log_file = 'mpvBookmark.log', --name+extension of the file that will be used to store the log data
file_title_logging = 'all', --Change between 'all', 'protocols', 'local', 'none'. This option will store the media title in log file, it is useful for websites / protocols because title cannot be parsed from links alone
logging_protocols=[[
["https?://", "magnet:", "rtmp:"]
]], --add above (after a comma) any protocol you want its title to be stored in the log file. This is valid only for (file_title_logging = 'protocols' or file_title_logging = 'all')
same_entry_limit = -1, --Limit saving entries with same path: -1 for unlimited, 0 will always update entries of same path, e.g. value of 3 will have the limit of 3 then it will start updating old values on the 4th entry.
overwrite_preserve_properties = true, --true is to preserve groups / slots or any other property when an entry is overwritten.
-----List Settings-----
loop_through_list = false, --true is for going up on the first item loops towards the last item and vise-versa. false disables this behavior.
list_middle_loader = true, --false is for more items to show, then u must reach the end. true is for new items to show after reaching the middle of list.
quickselect_0to9_keybind = false, --Keybind entries from 0 to 9 for quick selection when list is open (list_show_amount = 10 is maximum for this feature to work)
main_list_keybind_twice_exits = true, --Will exit the list when double tapping the main list, even if the list was accessed through a different filter.
search_not_typing_smartly = true, --To smartly set the search as not typing (when search box is open) without needing to press ctrl+enter.
search_behavior = 'any', --'any' to find any typed search based on combination of date, title, path / url, and time. 'any-notime' to find any typed search based on combination of date, title, and path / url, but without looking for time (this is to reduce unwanted results).
-----Filter Settings------
filters_and_sequence=[[
["all", "keybinds", "groups", "/:group%TV Shows%", "/:group%Movies%", "/:group%Anime%", "/:group%Anime Movies%", "/:group%Cartoon%", "/:group%Animated Movies%", "protocols", "fileonly", "titleonly", "timeonly", "playing", "keywords", "recents", "distinct", "keybinds%+%groups", "all%-%groups%-%keybinds"]
]], --Jump to the following filters and in the shown sequence when navigating via left and right keys. You can change the sequence and delete filters that are not needed.
next_filter_sequence_keybind=[[
["RIGHT", "MBTN_FORWARD"]
]],--Keybind that will be used to go to the next available filter based on the filters_and_sequence
previous_filter_sequence_keybind=[[
["LEFT", "MBTN_BACK"]
]],--Keybind that will be used to go to the previous available filter based on the filters_and_sequence
loop_through_filters = true, --true is for bypassing the last filter to go to first filter when navigating through filters using arrow keys, and vice-versa. false disables this behavior.
keywords_filter_list=[[
[]
]], --Create a filter out of your desired 'keywords', e.g.: youtube.com will filter out the videos from youtube. You can also insert a portion of filename or title, or extension or a full path / portion of a path. e.g.: ["youtube.com", "mp4", "naruto", "c:\\users\\eisa01\\desktop"]. To disable this filter keep it empty []
-----Sort Settings------
--Available sorts: 'added-asc', 'added-desc', 'time-asc', 'time-desc', 'alphanum-asc', 'alphanum-desc'
--Sorts description: 'added-asc' is for the newest added item to show first. Or 'added-desc' for the newest added to show last. Or 'alphanum-asc' is for A to Z approach with filename and episode number lower first. Or 'alphanum-desc' is for its Z to A approach. Or 'time-asc', 'time-desc' to sort the list based on time.
list_default_sort = 'added-asc', --the default sorting method for all the different filters in the list. Choose between available sorts.
list_filters_sort=[[
[ ["keybinds", "keybind-asc"], ["fileonly", "alphanum-asc"], ["playing", "time-asc"] ]
]], --Default sort for specific filters, e.g.: [ ["all", "alphanum-asc"], ["playing", "added-desc"] ]
list_cycle_sort_keybind=[[
["alt+s", "alt+S"]
]], --Keybind to cycle through the different available sorts when list is open
-----List Design Settings-----
list_alignment = 7, --The alignment for the list, uses numpad positions choose from 1-9 or 0 to disable. e,g.:7 top left alignment, 8 top middle alignment, 9 top right alignment.
slice_name = false, --Change to true or false. Slices long names per the amount specified below
slice_name_amount = 55, --Amount for slicing long names (for path, name, and title) list_content_text variables
list_show_amount = 10, --Change maximum number to show items at once
list_content_text = '%number%. %name%%0_duration%%duration%%0_keybind%%keybind%%0_group%%group%%1_group%\\h\\N\\N', --Text to be shown as header for the list
--list_content_text variables: %quickselect%, %number%, %name%, %title%, %path%, %duration%, %length%, %remaining%, %dt%, %dt_"format%"%
--Variables explanation: %quickselect%: keybind for quickselect. %number%: numbered sequence of the item position. %name%: shows the file name. %title%: shows file title. %path%: shows the filepath or url. %duration%: the reached playback time of item. %length%: the total time length of the file. %remaining% the remaining playback time of file. %dt%: the logged date and time.
--You can also use %dt_"format%"%" as per lua date formatting (https://www.lua.org/pil/22.1.html). It is specified after dt_ ..example: (%dt_%a% %dt_%b% %dt_%y%) for abbreviated day month year
list_content_variables=[[
[ ["0_duration", " 🕒 "], ["0_keybind", " ⌨ "], ["0_group", " 🖿 "] ]
]], --User defined variables that only displays if the related variable is triggered.
--#_group, #_keybind, #_duration, #_length, #_remaining, #_dt. (# represents the possibility of creating many variables using different numbers. e.g.: "0_keybind", "1_keybind")
list_sliced_prefix = '...\\h\\N\\N', --The text that indicates there are more items above. \\N is for new line. \\h is for hard space.
list_sliced_suffix = '...', --The text that indicates there are more items below.
text_color = 'ffffff', --Text color for list in BGR hexadecimal
text_scale = 50, --Font size for the text of list
text_border = 0.7, --Black border size for the text of list
text_cursor_color = 'ffbf7f', --Text color of current cursor position in BGR hexadecimal
text_cursor_scale = 50, --Font size for text of current cursor position in list
text_cursor_border = 0.7, --Black border size for text of current cursor position in list
text_highlight_pre_text = '✅ ', --Pre text for highlighted multi-select item
search_color_typing = '00bfff', --Search color when in typing mode
search_color_not_typing = 'ffffaa', --Search color when not in typing mode and it is active
header_color = 'ffffaa', --Header color in BGR hexadecimal
header_scale = 55, --Header text size for the list
header_border = 0.8, --Black border size for the Header of list
header_text = '🔖 Bookmarks [%cursor%/%total%]%0_highlight%%highlight%%0_filter%%filter%%1_filter%%0_sort%%sort%%1_sort%%0_search%%search%%1_search%\\h\\N\\N', --The formatting of the items when you open the list
--header_text variables: %cursor%, %total%, %highlight%, %filter%, %search%, %duration%, %length%, %remaining%.
--Variables explanation: %cursor%: the number of cursor position. %total%: total amount in current list. %highlight%: total number of highlighted items. %filter%: shows the filter name, %search%: shows the typed search. %duration%: the total reached playback time of all displayed items. %length%: the total time length of the file for all displayed items. %remaining% the remaining playback time of file for all the displayed items.
header_variables=[[
[ ["0_highlight", "✅"], ["0_filter", " [Filter: "], ["1_filter", "]"], ["0_sort", " \\{"], ["1_sort", "}"], ["0_search", "\\h\\N\\N[Search="], ["1_search", "..]"] ]
]], --User defined variables that only displays if the related variable is triggered.
--#_filter, #_sort, #_highlight, #_search, #_duration, #_length%, #_remaining. (# represents the possibility of creating many variables using different numbers. e.g.: "0_filter", "1_filter")
header_sort_hide_text = 'added-asc',--Sort method that is hidden from header when using %sort% variable
-----Time Format Settings-----
--in the first parameter, you can define from the available styles: default, hms, hms-full, timestamp, timestamp-concise "default" to show in HH:MM:SS.sss format. "hms" to show in 1h 2m 3.4s format. "hms-full" is the same as hms but keeps the hours and minutes persistent when they are 0. "timestamp" to show the total time as timestamp 123456.700 format. "timestamp-concise" shows the total time in 123456.7 format (shows and hides decimals depending on availability).
--in the second parameter, you can define whether to show milliseconds, round them or truncate them. Available options: 'truncate' to remove the milliseconds and keep the seconds. 0 to remove the milliseconds and round the seconds. 1 or above is the amount of milliseconds to display. The default value is 3 milliseconds.
--in the third parameter you can define the seperator between hour:minute:second. "default" style is automatically set to ":", "hms", "hms-full" are automatically set to " ". You can define your own. Some examples: ["default", 3, "-"],["hms-full", 5, "."],["hms", "truncate", ":"],["timestamp-concise"],["timestamp", 0],["timestamp", "truncate"],["timestamp", 5]
osd_time_format=[[
["default", "truncate"]
]],
list_duration_time_format=[[
["default", "truncate"]
]],
list_length_time_format=[[
["default", "truncate"]
]],
list_remaining_time_format=[[
["default", "truncate"]
]],
header_duration_time_format=[[
["hms", "truncate", ":"]
]],
header_length_time_format=[[
["hms", "truncate", ":"]
]],
header_remaining_time_format=[[
["hms", "truncate", ":"]
]],
-----List Keybind Settings-----
--Add below (after a comma) any additional keybind you want to bind. Or change the letter inside the quotes to change the keybind
--Example of changing and adding keybinds: --From ["b", "B"] To ["b"]. --From [""] to ["alt+b"]. --From [""] to ["a" "ctrl+a", "alt+a"]
list_move_up_keybind=[[
["UP", "WHEEL_UP"]
]], --Keybind that will be used to navigate up on the list
list_move_down_keybind=[[
["DOWN", "WHEEL_DOWN"]
]], --Keybind that will be used to navigate down on the list
list_page_up_keybind=[[
["PGUP"]
]], --Keybind that will be used to go to the first item for the page shown on the list
list_page_down_keybind=[[
["PGDWN"]
]], --Keybind that will be used to go to the last item for the page shown on the list
list_move_first_keybind=[[
["HOME"]
]], --Keybind that will be used to navigate to the first item on the list
list_move_last_keybind=[[
["END"]
]], --Keybind that will be used to navigate to the last item on the list
list_highlight_move_keybind=[[
["SHIFT"]
]], --Keybind that will be used to highlight while pressing a navigational keybind, keep holding shift and then press any navigation keybind, such as: up, down, home, pgdwn, etc..
list_highlight_all_keybind=[[
["ctrl+a", "ctrl+A"]
]], --Keybind that will be used to highlight all displayed items on the list
list_unhighlight_all_keybind=[[
["ctrl+d", "ctrl+D"]
]], --Keybind that will be used to remove all currently highlighted items from the list
list_select_keybind=[[
["ENTER", "MBTN_MID"]
]], --Keybind that will be used to load entry based on cursor position
list_add_playlist_keybind=[[
["CTRL+ENTER"]
]], --Keybind that will be used to add entry to playlist based on cursor position
list_add_playlist_highlighted_keybind=[[
["SHIFT+ENTER"]
]], --Keybind that will be used to add all highlighted entries to playlist
list_close_keybind=[[
["ESC", "MBTN_RIGHT"]
]], --Keybind that will be used to close the list (closes search first if it is open)
list_delete_keybind=[[
["DEL"]
]], --Keybind that will be used to delete the entry based on cursor position
list_delete_highlighted_keybind=[[
["SHIFT+DEL"]
]], --Keybind that will be used to delete all highlighted entries from the list
list_search_activate_keybind=[[
["ctrl+f", "ctrl+F"]
]], --Keybind that will be used to trigger search
list_search_not_typing_mode_keybind=[[
["ALT+ENTER"]
]], --Keybind that will be used to exit typing mode of search while keeping search open
list_ignored_keybind=[[
["h", "H", "r", "R", "c", "C"]
]], --Keybind thats are ignored when list is open
---------------------------END OF USER CUSTOMIZATION SETTINGS---------------------------
}
(require 'mp.options').read_options(o)
local utils = require 'mp.utils'
local msg = require 'mp.msg'
o.filters_and_sequence = utils.parse_json(o.filters_and_sequence)
o.keywords_filter_list = utils.parse_json(o.keywords_filter_list)
o.list_filters_sort = utils.parse_json(o.list_filters_sort)
o.logging_protocols = utils.parse_json(o.logging_protocols)
o.osd_time_format = utils.parse_json(o.osd_time_format)
o.list_duration_time_format = utils.parse_json(o.list_duration_time_format)--1.3# changed and added time format for each in the list
o.list_length_time_format = utils.parse_json(o.list_length_time_format)--1.3# added time format for each in the list
o.list_remaining_time_format = utils.parse_json(o.list_remaining_time_format)--1.3# added time format for each in the list
o.header_duration_time_format = utils.parse_json(o.header_duration_time_format)
o.header_length_time_format = utils.parse_json(o.header_length_time_format)
o.header_remaining_time_format = utils.parse_json(o.header_remaining_time_format)
o.bookmark_save_keybind = utils.parse_json(o.bookmark_save_keybind)
o.bookmark_fileonly_keybind = utils.parse_json(o.bookmark_fileonly_keybind)
o.keybinds_add_load_keybind = utils.parse_json(o.keybinds_add_load_keybind)
o.keybinds_remove_keybind = utils.parse_json(o.keybinds_remove_keybind)
o.keybinds_remove_highlighted_keybind = utils.parse_json(o.keybinds_remove_highlighted_keybind)
o.keybinds_quicksave_keybind = utils.parse_json(o.keybinds_quicksave_keybind)
o.groups_list_and_keybind = utils.parse_json(o.groups_list_and_keybind)
o.list_groups_remove_keybind = utils.parse_json(o.list_groups_remove_keybind)
o.list_groups_remove_highlighted_keybind = utils.parse_json(o.list_groups_remove_highlighted_keybind)
o.list_group_add_cycle_keybind = utils.parse_json(o.list_group_add_cycle_keybind)
o.list_group_add_cycle_highlighted_keybind = utils.parse_json(o.list_group_add_cycle_highlighted_keybind)
o.list_move_up_keybind = utils.parse_json(o.list_move_up_keybind)
o.list_move_down_keybind = utils.parse_json(o.list_move_down_keybind)
o.list_page_up_keybind = utils.parse_json(o.list_page_up_keybind)
o.list_page_down_keybind = utils.parse_json(o.list_page_down_keybind)
o.list_move_first_keybind = utils.parse_json(o.list_move_first_keybind)
o.list_move_last_keybind = utils.parse_json(o.list_move_last_keybind)
o.list_highlight_move_keybind = utils.parse_json(o.list_highlight_move_keybind)
o.list_highlight_all_keybind = utils.parse_json(o.list_highlight_all_keybind)
o.list_unhighlight_all_keybind = utils.parse_json(o.list_unhighlight_all_keybind)
o.list_cycle_sort_keybind = utils.parse_json(o.list_cycle_sort_keybind)
o.list_content_variables = utils.parse_json(o.list_content_variables)--1.3# for new config
o.header_variables = utils.parse_json(o.header_variables)--1.3# for new config
o.list_select_keybind = utils.parse_json(o.list_select_keybind)
o.list_add_playlist_keybind = utils.parse_json(o.list_add_playlist_keybind)
o.list_add_playlist_highlighted_keybind = utils.parse_json(o.list_add_playlist_highlighted_keybind)
o.list_close_keybind = utils.parse_json(o.list_close_keybind)
o.list_delete_keybind = utils.parse_json(o.list_delete_keybind)
o.list_delete_highlighted_keybind = utils.parse_json(o.list_delete_highlighted_keybind)
o.list_search_activate_keybind = utils.parse_json(o.list_search_activate_keybind)
o.list_search_not_typing_mode_keybind = utils.parse_json(o.list_search_not_typing_mode_keybind)
o.next_filter_sequence_keybind = utils.parse_json(o.next_filter_sequence_keybind)
o.previous_filter_sequence_keybind = utils.parse_json(o.previous_filter_sequence_keybind)
o.open_list_keybind = utils.parse_json(o.open_list_keybind)
o.list_filter_jump_keybind = utils.parse_json(o.list_filter_jump_keybind)
o.list_ignored_keybind = utils.parse_json(o.list_ignored_keybind)
if utils.shared_script_property_set then
utils.shared_script_property_set('simplebookmark-menu-open', 'no')
end
mp.set_property('user-data/simplebookmark/menu-open', 'no')
if string.lower(o.log_path) == '/:dir%mpvconf%' then
o.log_path = mp.find_config_file('.')
elseif string.lower(o.log_path) == '/:dir%script%' then
o.log_path = debug.getinfo(1).source:match('@?(.*/)')
elseif o.log_path:match('/:var%%(.*)%%') then
local os_variable = o.log_path:match('/:var%%(.*)%%')
o.log_path = o.log_path:gsub('/:var%%(.*)%%', os.getenv(os_variable))
end
local log_fullpath = utils.join_path(o.log_path, o.log_file)
local log_length_text = 'length='
local log_time_text = 'time='
local log_keybind_text = 'slot='
local log_group_text = 'group='
local protocols = {'https?:', 'magnet:', 'rtmps?:', 'smb:', 'ftps?:', 'sftp:'}
local available_sorts = {'added-asc', 'added-desc', 'time-asc', 'time-desc', 'alphanum-asc', 'alphanum-desc'}
local search_string = ''
local search_active = false
local loadTriggered = false --1.3.0# to identify if load is triggered atleast once for idle option
local resume_selected = false
local osd_log_contents = {}
local list_start = 0
local list_cursor = 1
local list_highlight_cursor = {}
local list_drawn = false
local list_pages = {}
local filePath, fileTitle, fileLength
local seekTime = 0
local filterName = 'all'
local sortName
function starts_protocol(tab, val)
for index, value in ipairs(tab) do
if (val:find(value) == 1) then
return true
end
end
return false
end
function contain_value(tab, val)
if not tab then return msg.error('check value passed') end
if not val then return msg.error('check value passed') end
for index, value in ipairs(tab) do
if value.match(string.lower(val), string.lower(value)) then
return true
end
end
return false
end
function has_value(tab, val, array2d)
if not tab then return msg.error('check value passed') end
if not val then return msg.error('check value passed') end
if not array2d then
for index, value in ipairs(tab) do
if string.lower(value) == string.lower(val) then
return true
end
end
end
if array2d then
for i=1, #tab do
if tab[i] and string.lower(tab[i][array2d]) == string.lower(val) then
return true
end
end
end
return false
end
function file_exists(name)
local f = io.open(name, "r")
if f ~= nil then io.close(f) return true else return false end
end
function format_time(seconds, sep, decimals, style)
local function divmod (a, b)
return math.floor(a / b), a % b
end
decimals = decimals == nil and 3 or decimals
local s = seconds
local h, s = divmod(s, 60*60)
local m, s = divmod(s, 60)
if decimals == 'truncate' then
s = math.floor(s)
decimals = 0
if style == 'timestamp' then
seconds = math.floor(seconds)
end
end
if not style or style == '' or style == 'default' then
local second_format = string.format("%%0%d.%df", 2+(decimals > 0 and decimals+1 or 0), decimals)
sep = sep and sep or ":"
return string.format("%02d"..sep.."%02d"..sep..second_format, h, m, s)
elseif style == 'hms' or style == 'hms-full' then
sep = sep ~= nil and sep or " "
if style == 'hms-full' or h > 0 then
return string.format("%dh"..sep.."%dm"..sep.."%." .. tostring(decimals) .. "fs", h, m, s)
elseif m > 0 then
return string.format("%dm"..sep.."%." .. tostring(decimals) .. "fs", m, s)
else
return string.format("%." .. tostring(decimals) .. "fs", s)
end
elseif style == 'timestamp' then
return string.format("%." .. tostring(decimals) .. "f", seconds)
elseif style == 'timestamp-concise' then
return seconds
end
end
function get_file() --1.3# removed prefer filename overtitle
local path = mp.get_property('path')
if not path then return end
local length = (mp.get_property_number('duration') or 0)
local title = mp.get_property('media-title'):gsub("\"", "")
return path, title, length
end
function get_local_names(target, property) --1.3# function to get names and fall back to whatever is found --1.2.4# removed or "" so that I can check for errors if the returned value is nil
local target_filename = target.found_name or target.found_title or target.found_path
local target_filepath = target.found_path or target.found_name or target.found_title
local target_filetitle = target.found_title or target.found_name or target.found_path
if not property then
return target_filename, target_filepath, target_filetitle
elseif property == 'osd' then --1.3# added osd property so it removes special character functions when displaying osd in mpv (uses gsub from search)
return esc_ass(target_filename), esc_ass(target_filepath), esc_ass(target_filetitle)
end
end
function get_slot_keybind(keyindex)
local keybind_return
if o.keybinds_add_load_keybind[keyindex] then
keybind_return = o.keybinds_add_load_keybind[keyindex]
else
keybind_return = log_keybind_text .. (keyindex or '') .. ' undefined'
end
return keybind_return
end
function get_group_properties(groupindex, action)
local gname, gkeybind, ghkeybind
if o.groups_list_and_keybind[groupindex] and o.groups_list_and_keybind[groupindex][1] then
gname = o.groups_list_and_keybind[groupindex][1]
else
gname = log_group_text ..(groupindex or '').. ' undefined'
end
if o.groups_list_and_keybind[groupindex] and o.groups_list_and_keybind[groupindex][2] then
gkeybind = o.groups_list_and_keybind[groupindex][2]
else
gkeybind = log_group_text ..(groupindex or '').. ' undefined'
end
if o.groups_list_and_keybind[groupindex] and o.groups_list_and_keybind[groupindex][3] then
ghkeybind = o.groups_list_and_keybind[groupindex][3]
else
ghkeybind = log_group_text ..(groupindex or '').. ' undefined'
end
return {name = gname, keybind = gkeybind, highlight_keybind = ghkeybind}
end
function bind_keys(keys, name, func, opts)
if not keys then
mp.add_forced_key_binding(keys, name, func, opts)
return
end
for i = 1, #keys do
if i == 1 then
mp.add_forced_key_binding(keys[i], name, func, opts)
else
mp.add_forced_key_binding(keys[i], name .. i, func, opts)
end
end
end
function unbind_keys(keys, name)
if not keys then
mp.remove_key_binding(name)
return
end
for i = 1, #keys do
if i == 1 then
mp.remove_key_binding(name)
else
mp.remove_key_binding(name .. i)
end
end
end
function esc_string(str)
return str:gsub("([%p])", "%%%1")
end
function esc_ass(str) --1.3# used function to escape, also this function will use the byte order mark instead of immediately pasting the zero-width space
return str:gsub('\\', '\\\239\187\191'):gsub('{', '\\{')
end
---------Start of LogManager---------
--LogManager (Read and Format the List from Log)--
function read_log(func)
local f = io.open(log_fullpath, "r")
if not f then return end
local contents = {}
local line_count = 0
for line in f:lines() do
table.insert(contents, (func(line)))
end
f:close()
return contents
end
function read_log_table()
local line_pos = 0
return read_log(function(line)
local tt, p, t, s, d, n, e, l, dt, ln, r, g
if line:match('^.-\"(.-)\"') then --1.3# changed if statement to only get title and path
tt, p = line:match('^.-\"(.-)\" | (.*) | ' .. esc_string(log_length_text) .. '(.*)')
else --1.3# get path only if no title is there
p = line:match('[(.*)%]]%s(.*) | ' .. esc_string(log_length_text) .. '(.*)')
end
d, n, e = p:match('^(.-)([^\\/]-)%.([^\\/%.]-)%.?$') --1.3# not inside if statement anymore since we are not changing name with title anymore
dt = line:match('%[(.-)%]')
t = line:match(' | ' .. esc_string(log_time_text) .. '(%d*%.?%d*)(.*)$')
ln = line:match(' | ' .. esc_string(log_length_text) .. '(%d*%.?%d*)(.*)$')
if tonumber(ln) and tonumber(t) then r = tonumber(ln) - tonumber(t) else r = 0 end
s = line:match(' | .* | ' .. esc_string(log_keybind_text) .. '(%d*)(.*)$')
g = line:match(' | .* | ' .. esc_string(log_group_text) .. '(%d*)(.*)$')
l = line
line_pos = line_pos + 1
return {found_path = p, found_time = t, found_name = n, found_title = tt, found_line = l, found_sequence = line_pos, found_directory = d, found_datetime = dt, found_length = ln, found_remaining = r, found_slot = s, found_group = g}
end)
end
function list_sort(tab, sort)
if sort == 'added-asc' then
table.sort(tab, function(a, b) return a['found_sequence'] < b['found_sequence'] end)
elseif sort == 'added-desc' then
table.sort(tab, function(a, b) return a['found_sequence'] > b['found_sequence'] end)
elseif sort == 'keybind-asc' and filterName == 'keybinds' then
table.sort(tab, function(a, b) return a['found_slot'] > b['found_slot'] end)
elseif sort == 'keybind-desc' and filterName == 'keybinds' then
table.sort(tab, function(a, b) return a['found_slot'] < b['found_slot'] end)
elseif sort == 'time-asc' then
table.sort(tab, function(a, b) return tonumber(a['found_time']) > tonumber(b['found_time']) end)
elseif sort == 'time-desc' then
table.sort(tab, function(a, b) return tonumber(a['found_time']) < tonumber(b['found_time']) end)
elseif sort == 'alphanum-asc' or sort == 'alphanum-desc' then
local function padnum(d) local dec, n = string.match(d, "(%.?)0*(.+)")
return #dec > 0 and ("%.12f"):format(d) or ("%s%03d%s"):format(dec, #n, n) end
if sort == 'alphanum-asc' then
table.sort(tab, function(a, b) return tostring(a['found_path']):lower():gsub("%.?%d+", padnum) .. ("%3d"):format(#b) > tostring(b['found_path']):lower():gsub("%.?%d+", padnum) .. ("%3d"):format(#a) end)
elseif sort == 'alphanum-desc' then
table.sort(tab, function(a, b) return tostring(a['found_path']):lower():gsub("%.?%d+", padnum) .. ("%3d"):format(#b) < tostring(b['found_path']):lower():gsub("%.?%d+", padnum) .. ("%3d"):format(#a) end)
end
end
return tab
end
function get_o_variable(str, arr_var) --1.3# function to get variable content from passed array
if not str then return end
if str:match('%%(.*)%%') then str = str:match('%%(.*)%%') end --1.3# if the entry has % around it, then remove it
local var_return
for i = 1, #arr_var do --1.3# loop through the passed array and get the value of the matched variable
if arr_var[i][1] == str then
var_return = arr_var[i][2] --1.3# added or "" so if that the content of the variable is not defined it does not crash
break
end
end
return var_return or "" --1.3# return the founded variable content or empty string if nothing is found
end
function parse_list_item(str, properties) --1.3#add ability to parse the contents of the list like the header
if not str then return msg.error('str in parse_list_item is nil') end
local list_filename, list_filepath, list_filetitle = get_local_names(properties["item"],'osd')--1.3# added osd property so it removes special characters for displaying list
if o.slice_name and list_filepath:len() > o.slice_name_amount then --1.3.1# fix #86 since p doesn't exist anymore, and checks for specific filename / filepath / filetitle, so slicing is accurate.
list_filepath = list_filepath:sub(1, o.slice_name_amount) .. "..."
end
if o.slice_name and list_filename:len() > o.slice_name_amount then
list_filename = list_filename:sub(1, o.slice_name_amount) .. "..."
end
if o.slice_name and list_filetitle:len() > o.slice_name_amount then
list_filetitle = list_filetitle:sub(1, o.slice_name_amount) .. "..."
end
str = str:gsub("%%name%%", list_filename)
:gsub("%%path%%", list_filepath)
:gsub("%%title%%", list_filetitle)
:gsub("%%number%%", properties["index"]+1) --1.3# index +1 is the osd_index
:gsub("%%dt%%", properties["item"].found_datetime)
for s in str:gmatch("%%dt_%%.%%") do --1.3# loop through all found dt_ in the script a
local svar = s:match("_%%."):sub(2) --1.3# match whatever starting from _ when using %dt_var%, then sub(2) to remove the first letter which is _ (then var will remain) to use in our gsub
if parse_8601(properties["item"].found_datetime) then --1.3.1# for backward compatibility if matching did not work reset to null
str = str:gsub(esc_string(s), os.date(svar, parse_8601(properties["item"].found_datetime))) --1.3# replaces the found dt_var with eg. dt_%a from config
for x in str:gmatch("%%%d_dt%%") do --1.3.1# for backward compatibility adds 0_dt to be able to force customize date and time variables
str = str:gsub(esc_string(x), get_o_variable(x, o.list_content_variables))
end
else --1.3.1# for backward compatibility if matching did not work reset to null
str = str:gsub(esc_string(s), "")
for x in str:gmatch("%%%d_dt%%") do --1.3.1# for backward compatibility removes 0_dt if log time cannot be parsed from in log
str = str:gsub(esc_string(x), "")
end
end
end
if properties["item"].found_slot then
str = str:gsub("%%keybind%%", get_slot_keybind(tonumber(properties["item"].found_slot)))
for s in str:gmatch("%%%d*_keybind%%") do --1.3# if a custom group variable is found, such as %0_group% then get the content of the variable for it
str = str:gsub(esc_string(s), get_o_variable(s, o.list_content_variables))
end
else
str = str:gsub("%%keybind%%", "")
for s in str:gmatch("%%%d*_keybind%%") do --1.3# if a custom slot is found and there is no slot assigned, remove it
str = str:gsub(esc_string(s), "")
end
end
if properties["item"].found_group then
str = str:gsub("%%group%%", get_group_properties(tonumber(properties["item"].found_group)).name)
for s in str:gmatch("%%%d*_group%%") do --1.3# if a custom group variable is found, such as %0_group% then get the content of the variable for it
str = str:gsub(esc_string(s), get_o_variable(s, o.list_content_variables))
end
else
str = str:gsub("%%group%%", "")
for s in str:gmatch("%%%d*_group%%") do
str = str:gsub(esc_string(s), "")
end
end
if properties['quickselect'] and str:match("%%quickselect%%") then --1.2# replace quickselect with the actual key if its available
str = str:gsub("%%quickselect%%", properties['quickselect'])
else
str = str:gsub("%%quickselect%%", "")
end
--1.3# same concept for showing groups but for time
if properties["item"].found_time and tonumber(properties["item"].found_time) > 0 then
str = str:gsub('%%duration%%', format_time(properties["item"].found_time, o.list_duration_time_format[3], o.list_duration_time_format[2], o.list_duration_time_format[1]))
for s in str:gmatch("%%%d*_duration%%") do
str = str:gsub(esc_string(s), get_o_variable(s, o.list_content_variables))
end
else
str = str:gsub("%%duration%%", "")
for s in str:gmatch("%%%d*_duration%%") do
str = str:gsub(esc_string(s), "")
end
end
if properties["item"].found_length and tonumber(properties["item"].found_length) > 0 then
str = str:gsub('%%length%%', format_time(properties["item"].found_length, o.list_length_time_format[3], o.list_length_time_format[2], o.list_length_time_format[1]))
for s in str:gmatch("%%%d*_length%%") do
str = str:gsub(esc_string(s), get_o_variable(s, o.list_content_variables))
end
else
str = str:gsub("%%length%%", "")
for s in str:gmatch("%%%d*_length%%") do
str = str:gsub(esc_string(s), "")
end
end
if properties["item"].found_remaining and tonumber(properties["item"].found_remaining) > 0 then
str = str:gsub('%%remaining%%', format_time(properties["item"].found_remaining, o.list_remaining_time_format[3], o.list_remaining_time_format[2], o.list_remaining_time_format[1]))
for s in str:gmatch("%%%d*_remaining%%") do
str = str:gsub(esc_string(s), get_o_variable(s, o.list_content_variables))
end
else
str = str:gsub("%%remaining%%", "")
for s in str:gmatch("%%%d*_remaining%%") do
str = str:gsub(esc_string(s), "")
end
end
str = str:gsub("%%%%", "%%")
return str
end
function parse_header(str)
local osd_header_color = string.format("{\\1c&H%s}", o.header_color)
local osd_search_color = osd_header_color
if search_active == 'typing' then
osd_search_color = string.format("{\\1c&H%s}", o.search_color_typing)
elseif search_active == 'not_typing' then
osd_search_color = string.format("{\\1c&H%s}", o.search_color_not_typing)
end
str = str:gsub("%%total%%", #osd_log_contents)
:gsub("%%cursor%%", list_cursor)
local filter_osd = filterName
if filter_osd ~= 'all' then
if filter_osd:match('/:group%%(.*)%%') then filter_osd = filter_osd:match('/:group%%(.*)%%') end
str = str:gsub("%%filter%%", filter_osd)
for s in str:gmatch("%%%d*_filter%%") do --1.3# if a filter variable is found, such as %0_filter% then get the content of the variable for it
str = str:gsub(esc_string(s), get_o_variable(s, o.header_variables))
end
else
str = str:gsub("%%filter%%", '')
for s in str:gmatch("%%%d*_filter%%") do --1.3# if a custom slot is found and there is no slot assigned, remove it
str = str:gsub(esc_string(s), "")
end
end
if str:match('%%duration%%') then
if get_total_duration('found_time') > 0 then
str = str:gsub("%%duration%%", format_time(get_total_duration('found_time'), o.header_duration_time_format[3], o.header_duration_time_format[2], o.header_duration_time_format[1]))
for s in str:gmatch("%%%d*_duration%%") do --1.3# if a filter variable is found, such as %0_filter% then get the content of the variable for it
str = str:gsub(esc_string(s), get_o_variable(s, o.header_variables))
end
else
str = str:gsub("%%duration%%", '')
for s in str:gmatch("%%%d*_duration%%") do --1.3# if a custom slot is found and there is no slot assigned, remove it
str = str:gsub(esc_string(s), "")
end
end
end
if str:match('%%length%%') then
if get_total_duration('found_length') > 0 then
str = str:gsub("%%length%%", format_time(get_total_duration('found_length'), o.header_length_time_format[3], o.header_length_time_format[2], o.header_length_time_format[1]))
for s in str:gmatch("%%%d*_length%%") do --1.3# if a filter variable is found, such as %0_filter% then get the content of the variable for it
str = str:gsub(esc_string(s), get_o_variable(s, o.header_variables))
end
else
str = str:gsub("%%length%%", '')
for s in str:gmatch("%%%d*_length%%") do --1.3# if a filter variable is found, such as %0_filter% then get the content of the variable for it
str = str:gsub(esc_string(s), "")
end
end
end
if str:match('%remaining%%') then
if get_total_duration('found_remaining') > 0 then
str = str:gsub("%%remaining%%", format_time(get_total_duration('found_remaining'), o.header_remaining_time_format[3], o.header_remaining_time_format[2], o.header_remaining_time_format[1]))
for s in str:gmatch("%%%d*_remaining%%") do --1.3# if a filter variable is found, such as %0_filter% then get the content of the variable for it
str = str:gsub(esc_string(s), get_o_variable(s, o.header_variables))
end
else
str = str:gsub("%%remaining%%", '')
for s in str:gmatch("%%%d*_remaining%%") do --1.3# if a filter variable is found, such as %0_filter% then get the content of the variable for it
str = str:gsub(esc_string(s), "")
end
end
end
if #list_highlight_cursor > 0 then
str = str:gsub("%%highlight%%", #list_highlight_cursor)
for s in str:gmatch("%%%d*_highlight%%") do --1.3# if a filter variable is found, such as %0_filter% then get the content of the variable for it
str = str:gsub(esc_string(s), get_o_variable(s, o.header_variables))
end
else
str = str:gsub("%%highlight%%", '')
for s in str:gmatch("%%%d*_highlight%%") do --1.3# if a filter variable is found, such as %0_filter% then get the content of the variable for it
str = str:gsub(esc_string(s), "")
end
end
if sortName and sortName ~= o.header_sort_hide_text then
str = str:gsub("%%sort%%", sortName)
for s in str:gmatch("%%%d*_sort%%") do --1.3# if a filter variable is found, such as %0_filter% then get the content of the variable for it
str = str:gsub(esc_string(s), get_o_variable(s, o.header_variables))
end
else
str = str:gsub("%%sort%%", '')
for s in str:gmatch("%%%d*_sort%%") do --1.3# if a filter variable is found, such as %0_filter% then get the content of the variable for it
str = str:gsub(esc_string(s), "")
end
end
if search_active then
local search_string_osd = search_string
if search_string_osd ~= '' then
search_string_osd = esc_ass(search_string:gsub('%%', '%%%%%%%%')) --1.3# used ass_escape instead of gsub
end
str = str:gsub("%%search%%", osd_search_color..search_string_osd..osd_header_color)
for s in str:gmatch("%%%d*_search%%") do --1.3# if a filter variable is found, such as %0_filter% then get the content of the variable for it
str = str:gsub(esc_string(s), get_o_variable(s, o.header_variables))
end
else
str = str:gsub("%%search%%", '')
for s in str:gmatch("%%%d*_search%%") do --1.3# if a filter variable is found, such as %0_filter% then get the content of the variable for it
str = str:gsub(esc_string(s), "")
end
end
str = str:gsub("%%%%", "%%")
return str
end
function search_log_contents(arr_contents)
if not arr_contents or not arr_contents[1] or not search_active or not search_string == '' then return false end
local search_query = ''
for search in search_string:gmatch("[^%s]+") do
search_query = search_query..'.-'..esc_string(search)
end
local contents_string = ''
local search_arr_contents = {}
for i = 1, #arr_contents do --1.3# removed specific search method as it doesn't seem useful anymore, --1.3.1# utilize arr_contents instead of osd_log_contents
if o.search_behavior == 'any' then
contents_string = arr_contents[i].found_datetime --1.3# seperated date and time for search
if parse_8601(arr_contents[i].found_datetime) then --1.3# an if statement to check if date could be parsed --1.3# allows for all type of dates to be searched thanks to the loop
local os_date_tag= {'%a', '%A', '%b', '%B', '%c', '%d', '%H', '%I', '%M', '%m', '%p', '%S', '%w', '%x', '%X', '%Y', '%y'} --1.3# add all lua date time parameters
for j=1, #os_date_tag do --1.3# replace all lua parameters with date values that can be searched
contents_string = contents_string..os.date(os_date_tag[j], parse_8601(arr_contents[i].found_datetime))
end
end
contents_string = contents_string..(arr_contents[i].found_title or '')..(arr_contents[i].found_name or '')..arr_contents[i].found_path --1.3# added found_name since parsing is different now
if tonumber(arr_contents[i].found_time) > 0 then
contents_string = contents_string..format_time(arr_contents[i].found_time, o.list_duration_time_format[3], o.list_duration_time_format[2], o.list_duration_time_format[1])
end
if tonumber(arr_contents[i].found_length) > 0 then
contents_string = contents_string..format_time(arr_contents[i].found_length, o.list_length_time_format[3], o.list_length_time_format[2], o.list_length_time_format[1])
end
if tonumber(arr_contents[i].found_remaining) > 0 then
contents_string = contents_string..format_time(arr_contents[i].found_remaining, o.list_remaining_time_format[3], o.list_remaining_time_format[2], o.list_remaining_time_format[1])
end
if arr_contents[i].found_slot then
contents_string = contents_string..get_slot_keybind(tonumber(arr_contents[i].found_slot))
end
if arr_contents[i].found_group then
contents_string = contents_string..get_group_properties(tonumber(arr_contents[i].found_group)).name
end
elseif o.search_behavior == 'any-notime' then
contents_string = arr_contents[i].found_datetime --1.3# seperated date and time for search
if parse_8601(arr_contents[i].found_datetime) then --1.3# an if statement to check if date could be parsed
local os_date_tag= {'%a', '%A', '%b', '%B', '%c', '%d', '%H', '%I', '%M', '%m', '%p', '%S', '%w', '%x', '%X', '%Y', '%y'} --1.3# add all lua date time parameters
for j=1, #os_date_tag do --1.3# replace all lua parameters with values that can be searched
contents_string = contents_string..os.date(os_date_tag[j], parse_8601(arr_contents[i].found_datetime))
end
end
contents_string = contents_string..(arr_contents[i].found_title or '')..(arr_contents[i].found_name or '')..arr_contents[i].found_path --1.3# added found_name since parsing is different now
if arr_contents[i].found_slot then
contents_string = contents_string..get_slot_keybind(tonumber(arr_contents[i].found_slot))
end
if arr_contents[i].found_group then
contents_string = contents_string..get_group_properties(tonumber(arr_contents[i].found_group)).name
end
end
if string.lower(contents_string):match(string.lower(search_query)) then
table.insert(search_arr_contents, arr_contents[i])
end
end
return search_arr_contents
end
function filter_log_contents(arr_contents, filter)
if not arr_contents or not arr_contents[1] or not filter or filter == 'all' then return false end
local filtered_arr_contents = {}
if filter:match('%%%+%%') then
if filter_stack(arr_contents,filter) then filtered_arr_contents = filter_stack(arr_contents, filter) end
elseif filter:match('%%%-%%') then
if filter_omit(arr_contents,filter) then filtered_arr_contents = filter_omit(arr_contents, filter) end
else
if filter_apply(arr_contents, filter) then filtered_arr_contents = filter_apply(arr_contents, filter) end
end
return filtered_arr_contents
end
function filter_omit(arr_contents, filter)
if not arr_contents or not arr_contents[1] or not filter or filter == 'all' or not filter:match('%%%-%%') then return false end
local omitted_arr_table = arr_contents
local filter_items = {}
for f in filter:gmatch("[^%%%-%%\r+]+") do
table.insert(filter_items, f)
end
local temp_filtered_contents = arr_contents
for i=1, #filter_items do
if i== 1 and filter_apply(arr_contents, filter_items[i]) then omitted_arr_table = filter_apply(arr_contents, filter_items[i]) end
if i > 1 then
if filter_apply(arr_contents, filter_items[i]) then temp_filtered_contents = filter_apply(arr_contents, filter_items[i]) end
for j=1, #temp_filtered_contents do
for k=1, #omitted_arr_table do
if temp_filtered_contents[j] and omitted_arr_table[k] and temp_filtered_contents[j].found_sequence == omitted_arr_table[k].found_sequence then
table.remove(omitted_arr_table, k)
end
end
end
end
end
table.sort(omitted_arr_table, function(a, b) return a['found_sequence'] < b['found_sequence'] end)
return omitted_arr_table
end
function filter_stack(arr_contents, filter)
if not arr_contents or not arr_contents[1] or not filter or filter == 'all' or not filter:match('%%%+%%') then return false end
local stacked_arr_table = {}
local filter_items = {}
for f in filter:gmatch("[^%%%+%%\r+]+") do
table.insert(filter_items, f)
end
local unique_values = {}
local temp_filtered_contents = arr_contents
for i=1, #filter_items do
if filter_apply(arr_contents, filter_items[i]) then temp_filtered_contents = filter_apply(arr_contents, filter_items[i]) end
for j=1, #temp_filtered_contents do
if not has_value(unique_values, temp_filtered_contents[j].found_sequence) then
table.insert(unique_values, temp_filtered_contents[j].found_sequence)
table.insert(stacked_arr_table, temp_filtered_contents[j])
end
end
end
table.sort(stacked_arr_table, function(a, b) return a['found_sequence'] < b['found_sequence'] end)
return stacked_arr_table
end
function filter_apply(arr_contents, filter)
if not arr_contents or not arr_contents[1] or not filter or filter == 'all' then return false end
local filtered_arr_contents = {}
if filter == 'groups' then
for i = 1, #arr_contents do
if arr_contents[i].found_group then
table.insert(filtered_arr_contents, arr_contents[i])
end
end
end
if filter:match('/:group%%(.*)%%') then
filter = filter:match('/:group%%(.*)%%')
for i = 1, #arr_contents do
if arr_contents[i].found_group and filter == get_group_properties(tonumber(arr_contents[i].found_group)).name then
table.insert(filtered_arr_contents, arr_contents[i])
end
end
end
if filter == 'keybinds' then
for i = 1, #arr_contents do
if arr_contents[i].found_slot then
table.insert(filtered_arr_contents, arr_contents[i])
end
end
end
if filter == 'recents' then
table.sort(arr_contents, function(a, b) return a['found_sequence'] < b['found_sequence'] end)
local unique_values = {}
local list_total = #arr_contents
if filePath == arr_contents[#arr_contents].found_path and tonumber(arr_contents[#arr_contents].found_time) == 0 then
list_total = list_total -1
end
for i = list_total, 1, -1 do
if not has_value(unique_values, arr_contents[i].found_path) then
table.insert(unique_values, arr_contents[i].found_path)
table.insert(filtered_arr_contents, arr_contents[i])
end
end
table.sort(filtered_arr_contents, function(a, b) return a['found_sequence'] < b['found_sequence'] end)
end
if filter == 'distinct' then
table.sort(arr_contents, function(a, b) return a['found_sequence'] < b['found_sequence'] end)
local unique_values = {}
local list_total = #arr_contents
if filePath == arr_contents[#arr_contents].found_path and tonumber(arr_contents[#arr_contents].found_time) == 0 then
list_total = list_total -1
end
for i = list_total, 1, -1 do
if arr_contents[i].found_directory and not has_value(unique_values, arr_contents[i].found_directory) and not starts_protocol(protocols, arr_contents[i].found_path) then
table.insert(unique_values, arr_contents[i].found_directory)
table.insert(filtered_arr_contents, arr_contents[i])