· 6 years ago · Jul 13, 2019, 05:38 AM
1-- // Bookmarker Menu v1.3.1 for mpv \\ --
2-- See README.md for instructions
3
4-- Maximum number of characters for bookmark name
5local maxChar = 100
6-- Number of bookmarks to be displayed per page
7local bookmarksPerPage = 10
8-- Whether to close the Bookmarker menu after loading a bookmark
9local closeAfterLoad = true
10-- Whether to close the Bookmarker menu after replacing a bookmark
11local closeAfterReplace = true
12-- Whether to ask for confirmation to replace a bookmark (Uses the Typer for confirmation)
13local confirmReplace = false
14-- Whether to ask for confirmation to delete a bookmark (Uses the Typer for confirmation)
15local confirmDelete = false
16-- The rate (in seconds) at which the bookmarker needs to refresh its interface; lower is more frequent
17local rate = 1.5
18-- The filename for the bookmarks file
19local bookmarkerName = "bookmarker.json"
20
21-- All the "global" variables and utilities; don't touch these
22local utils = require 'mp.utils'
23local styleOn = mp.get_property("osd-ass-cc/0")
24local styleOff = mp.get_property("osd-ass-cc/1")
25local bookmarks = {}
26local currentSlot = 0
27local currentPage = 1
28local maxPage = 1
29local active = false
30local mode = "none"
31local bookmarkStore = {}
32local oldSlot = 0
33
34-- // Controls \\ --
35
36-- List of custom controls and their function
37local bookmarkerControls = {
38 ESC = function() abort("") end,
39
40 DOWN = function() jumpSlot(1) end,
41 j = function() jumpSlot(1) end,
42 UP = function() jumpSlot(-1) end,
43 k = function() jumpSlot(-1) end,
44
45 n = function() jumpPage(1) end,
46 PGDWN = function() jumpPage(1) end,
47 m = function() jumpPage(-1) end,
48 PGUP = function() jumpPage(-1) end,
49
50 s = function() addBookmark() end,
51 S = function() mode="save" typerStart() end,
52 p = function() mode="replace" typerStart() end,
53 r = function() mode="rename" typerStart() end,
54 f = function() mode="filepath" typerStart() end,
55 d = function() mode="move" moverStart() end,
56
57 LEFT = function() mode="delete" typerStart() end,
58 h = function() mode="delete" typerStart() end,
59
60 ENTER = function() jumpToBookmark(currentSlot) end,
61 KP_ENTER = function() jumpToBookmark(currentSlot) end,
62 l = function() jumpToBookmark(currentSlot) end,
63 RIGHT = function() jumpToBookmark(currentSlot) end,
64}
65
66local bookmarkerFlags = {
67 DOWN = {repeatable = true},
68 UP = {repeatable = true},
69 RIGHT = {repeatable = true},
70 LEFT = {repeatable = true}
71}
72
73-- Activate the custom controls
74function activateControls(name, controls, flags)
75 for key, func in pairs(controls) do
76 mp.add_forced_key_binding(key, name..key, func, flags[key])
77 end
78end
79
80-- Deactivate the custom controls
81function deactivateControls(name, controls)
82 for key, _ in pairs(controls) do
83 mp.remove_key_binding(name..key)
84 end
85end
86
87-- // Typer \\ --
88
89-- Controls for the Typer
90local typerControls = {
91 ESC = function() typerExit() end,
92 ENTER = function() typerCommit() end,
93 KP_ENTER = function() typerCommit() end,
94 RIGHT = function() typerCursor(1) end,
95 LEFT = function() typerCursor(-1) end,
96 BS = function() typer("backspace") end,
97 DEL = function() typer("delete") end,
98 SPACE = function() typer(" ") end,
99 SHARP = function() typer("#") end,
100 KP0 = function() typer("0") end,
101 KP1 = function() typer("1") end,
102 KP2 = function() typer("2") end,
103 KP3 = function() typer("3") end,
104 KP4 = function() typer("4") end,
105 KP5 = function() typer("5") end,
106 KP6 = function() typer("6") end,
107 KP7 = function() typer("7") end,
108 KP8 = function() typer("8") end,
109 KP9 = function() typer("9") end,
110 KP_DEC = function() typer(".") end
111}
112
113-- All standard keys for the Typer
114local typerKeys = {"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","1","2","3","4","5","6","7","8","9","0","!","@","$","%","^","&","*","(",")","-","_","=","+","[","]","{","}","\\","|",";",":","'","\"",",",".","<",">","/","?","`","~","р","о","л","д","т","ь"}
115-- For some reason, semicolon is not possible, but it's listed there just in case anyway
116
117local typerText = ""
118local typerPos = 0
119local typerActive = false
120
121-- Function to activate the Typer
122-- use typerStart() for custom controls around activating the Typer
123function activateTyper()
124 for key, func in pairs(typerControls) do
125 mp.add_forced_key_binding(key, "typer"..key, func, {repeatable=true})
126 end
127 for i, key in ipairs(typerKeys) do
128 mp.add_forced_key_binding(key, "typer"..key, function() typer(key) end, {repeatable=true})
129 end
130 typerText = ""
131 typerActive = true
132end
133
134-- Function to deactivate the Typer
135-- use typerExit() for custom controls around deactivating the Typer
136function deactivateTyper()
137 for key, _ in pairs(typerControls) do
138 mp.remove_key_binding("typer"..key)
139 end
140 for i, key in ipairs(typerKeys) do
141 mp.remove_key_binding("typer"..key)
142 end
143 typerActive = false
144 return typerText
145end
146
147-- Function to move the cursor of the typer; can wrap around
148function typerCursor(direction)
149 typerPos = typerPos + direction
150 if typerPos < 0 then typerPos = typerText:len() end
151 if typerPos > typerText:len() then typerPos = 0 end
152 typer("")
153end
154
155-- Function for handling the text as it is being typed
156function typer(s)
157 -- Don't touch this part
158 if s == "backspace" then
159 if typerPos > 0 then
160 typerText = typerText:sub(1, typerPos - 1) .. typerText:sub(typerPos + 1)
161 typerPos = typerPos - 1
162 end
163 elseif s == "delete" then
164 if typerPos < typerText:len() then
165 typerText = typerText:sub(1, typerPos) .. typerText:sub(typerPos + 2)
166 end
167 else
168 if mode == "filepath" or typerText:len() < maxChar then
169 typerText = typerText:sub(1, typerPos) .. s .. typerText:sub(typerPos + 1)
170 typerPos = typerPos + s:len()
171 end
172 end
173
174 -- Enter custom script and display message here
175 local preMessage = "Enter a bookmark name:"
176 if mode == "save" then
177 preMessage = styleOn.."{\\b1}Save a new bookmark with custom name:{\\b0}"..styleOff
178 elseif mode == "replace" then
179 preMessage = styleOn.."{\\b1}Type \"y\" to replace the following bookmark:{\\b0}\n"..displayName(bookmarks[currentSlot]["name"])..styleOff
180 elseif mode == "delete" then
181 preMessage = styleOn.."{\\b1}Type \"y\" to delete the following bookmark:{\\b0}\n"..displayName(bookmarks[currentSlot]["name"])..styleOff
182 elseif mode == "rename" then
183 preMessage = styleOn.."{\\b1}Rename an existing bookmark:{\\b0}"..styleOff
184 elseif mode == "filepath" then
185 preMessage = styleOn.."{\\b1}Change the bookmark's filepath:{\\b0}"..styleOff
186 end
187
188 local postMessage = ""
189 local split = typerPos + math.floor(typerPos / maxChar)
190 local messageLines = math.floor((typerText:len() - 1) / maxChar) + 1
191 for i = 1, messageLines do
192 postMessage = postMessage .. typerText:sub((i-1) * maxChar + 1, i * maxChar) .. "\n"
193 end
194 postMessage = postMessage:sub(1,postMessage:len()-1)
195
196 mp.osd_message(preMessage.."\n"..postMessage:sub(1,split)..styleOn.."{\\c&H00FFFF&}{\\b1}|{\\r}"..styleOff..postMessage:sub(split+1), 9999)
197end
198
199-- // Mover \\ --
200
201-- Controls for the Mover
202local moverControls = {
203 ESC = function() moverExit() end,
204 DOWN = function() jumpSlot(1) end,
205 UP = function() jumpSlot(-1) end,
206 RIGHT = function() jumpPage(1) end,
207 LEFT = function() jumpPage(-1) end,
208 s = function() addBookmark() end,
209 m = function() moverCommit() end,
210 ENTER = function() moverCommit() end,
211 KP_ENTER = function() moverCommit() end
212}
213
214local moverFlags = {
215 DOWN = {repeatable = true},
216 UP = {repeatable = true},
217 RIGHT = {repeatable = true},
218 LEFT = {repeatable = true}
219}
220
221-- Function to activate the Mover
222function moverStart()
223 if bookmarkExists(currentSlot) then
224 deactivateControls("bookmarker", bookmarkerControls)
225 activateControls("mover", moverControls, moverFlags)
226 displayBookmarks()
227 else
228 abort(styleOn.."{\\c&H0000FF&}{\\b1}Can't find the bookmark at slot "..currentSlot)
229 end
230end
231
232-- Function to commit the action of the Mover
233function moverCommit()
234 saveBookmarks()
235 moverExit()
236end
237
238-- Function to deactivate the Mover
239-- If isError is set, then it'll abort
240function moverExit(isError)
241 deactivateControls("mover", moverControls)
242 mode = "none"
243 if not isError then
244 loadBookmarks()
245 displayBookmarks()
246 activateControls("bookmarker", bookmarkerControls, bookmarkerFlags)
247 end
248end
249
250-- // General utilities \\ --
251
252-- Check if the operating system is Mac OS
253function isMacOS()
254 local homedir = os.getenv("HOME")
255 return (homedir ~= nil and string.sub(homedir,1,6) == "/Users")
256end
257
258-- Check if the operating system is Windows
259function isWindows()
260 local windir = os.getenv("windir")
261 return (windir~=nil)
262end
263
264-- Check whether a certain file exists
265function fileExists(path)
266 local f = io.open(path,"r")
267 if f~=nil then
268 io.close(f)
269 return true
270 else
271 return false
272 end
273end
274
275-- Get the filepath of a file from the mpv config folder
276function getFilepath(filename)
277 if isWindows() then
278 return os.getenv("APPDATA"):gsub("\\", "/") .. "/mpv/" .. filename
279 else
280 return os.getenv("HOME") .. "/.config/mpv/" .. filename
281 end
282end
283
284-- Load a table from a JSON file
285-- Returns nil if the file can't be found
286function loadTable(path)
287 local contents = ""
288 local myTable = {}
289 local file = io.open( path, "r" )
290 if file then
291 local contents = file:read( "*a" )
292 myTable = utils.parse_json(contents);
293 io.close(file)
294 return myTable
295 end
296 return nil
297end
298
299-- Save a table as a JSON file file
300-- Returns true if successful
301function saveTable(t, path)
302 local contents = utils.format_json(t)
303 local file = io.open(path .. ".tmp", "wb")
304 file:write(contents)
305 io.close(file)
306 os.remove(path)
307 os.rename(path .. ".tmp", path)
308 return true
309end
310
311-- Convert a pos (seconds) to a hh:mm:ss.mmm format
312function parseTime(pos)
313 local hours = math.floor(pos/3600)
314 local minutes = math.floor((pos % 3600)/60)
315 local seconds = math.floor((pos % 60))
316 local milliseconds = math.floor(pos % 1 * 1000)
317 return string.format("%02d:%02d:%02d.%03d",hours,minutes,seconds,milliseconds)
318end
319
320-- // Bookmark functions \\ --
321
322-- Checks whether the specified bookmark exists
323function bookmarkExists(slot)
324 return (slot >= 1 and slot <= #bookmarks)
325end
326
327-- Calculates the current page and the total number of pages
328function calcPages()
329 currentPage = math.floor((currentSlot - 1) / bookmarksPerPage) + 1
330 if currentPage == 0 then currentPage = 1 end
331 maxPage = math.floor((#bookmarks - 1) / bookmarksPerPage) + 1
332 if maxPage == 0 then maxPage = 1 end
333end
334
335-- Get the amount of bookmarks on the specified page
336function getAmountBookmarksOnPage(page)
337 local n = bookmarksPerPage
338 if page == maxPage then n = #bookmarks % bookmarksPerPage end
339 if n == 0 then n = bookmarksPerPage end
340 if #bookmarks == 0 then n = 0 end
341 return n
342end
343
344-- Get the index of the first slot on the specified page
345function getFirstSlotOnPage(page)
346 return (page - 1) * bookmarksPerPage + 1
347end
348
349-- Get the index of the last slot on the specified page
350function getLastSlotOnPage(page)
351 local endSlot = getFirstSlotOnPage(page) + getAmountBookmarksOnPage(page) - 1
352 if endSlot > #bookmarks then endSlot = #bookmarks end
353 return endSlot
354end
355
356-- Jumps a certain amount of slots forward or backwards in the bookmarks list
357-- Keeps in mind if the current mode is to move bookmarks
358function jumpSlot(i)
359 if mode == "move" then
360 oldSlot = currentSlot
361 bookmarkStore = bookmarks[oldSlot]
362 end
363
364 currentSlot = currentSlot + i
365 local startSlot = getFirstSlotOnPage(currentPage)
366 local endSlot = getLastSlotOnPage(currentPage)
367
368 if currentSlot < startSlot then currentSlot = endSlot end
369 if currentSlot > endSlot then currentSlot = startSlot end
370
371 if mode == "move" then
372 table.remove(bookmarks, oldSlot)
373 table.insert(bookmarks, currentSlot, bookmarkStore)
374 end
375
376 displayBookmarks()
377end
378
379-- Jumps a certain amount of pages forward or backwards in the bookmarks list
380-- Keeps in mind if the current mode is to move bookmarks
381function jumpPage(i)
382 if mode == "move" then
383 oldSlot = currentSlot
384 bookmarkStore = bookmarks[oldSlot]
385 end
386
387 local oldPos = currentSlot - getFirstSlotOnPage(currentPage) + 1
388 currentPage = currentPage + i
389 if currentPage < 1 then currentPage = maxPage + currentPage end
390 if currentPage > maxPage then currentPage = currentPage - maxPage end
391
392 local bookmarksOnPage = getAmountBookmarksOnPage(currentPage)
393 if oldPos > bookmarksOnPage then oldPos = bookmarksOnPage end
394 currentSlot = getFirstSlotOnPage(currentPage) + oldPos - 1
395
396 if mode == "move" then
397 table.remove(bookmarks, oldSlot)
398 table.insert(bookmarks, currentSlot, bookmarkStore)
399 end
400
401 displayBookmarks()
402end
403
404-- Parses a bookmark name for storing, also trimming it
405-- Replaces %t with the timestamp of the bookmark
406-- Replaces %p with the time position of the bookmark
407function parseName(name)
408 local pos = 0
409 if mode == "rename" then pos = bookmarks[currentSlot]["pos"] else pos = mp.get_property_number("time-pos") end
410 name, _ = name:gsub("%%t", parseTime(pos))
411 name, _ = name:gsub("%%p", pos)
412 name = trimName(name)
413 return name
414end
415
416-- Parses a bookmark name for displaying, also trimming it
417-- Replaces all { with an escaped { so it won't be interpreted as a tag
418function displayName(name)
419 name, _ = name:gsub("{", "\\{")
420 name = trimName(name)
421 return name
422end
423
424-- Trims a name to the max number of characters
425function trimName(name)
426 if name:len() > maxChar then name = name:sub(1,maxChar) end
427 return name
428end
429
430-- Parses a Windows path with backslashes to one with normal slashes
431function parsePath(path)
432 if type(path) == "string" then path, _ = path:gsub("\\", "/") end
433 return path
434end
435
436-- Loads all the bookmarks in the global table and sets the current page and total number of pages
437-- Also checks for older versions of bookmarks and "updates" them
438-- Also checks for bookmarks made by "mpv-bookmarker" and converts them
439-- Also removes anything it doesn't recognize as a bookmark
440function loadBookmarks()
441 bookmarks = loadTable(getFilepath(bookmarkerName))
442 if bookmarks == nil then bookmarks = {} end
443
444 local doSave = false
445 local doEject = false
446 local doReplace = false
447 local ejects = {}
448 local newmarks = {}
449
450 for key, bookmark in pairs(bookmarks) do
451 if type(key) == "number" then
452 if bookmark.version == nil or bookmark.version == 1 then
453 if bookmark.name ~= nil and bookmark.path ~= nil and bookmark.pos ~= nil then
454 bookmark.path = parsePath(bookmark.path)
455 bookmark.version = 2
456 doSave = true
457 else
458 table.insert(ejects, key)
459 doEject = true
460 end
461 end
462 else
463 if bookmark.filename ~= nil and bookmark.pos ~= nil and bookmark.filepath ~= nil then
464 local newmark = {
465 name = trimName(""..bookmark.filename.." @ "..parseTime(bookmark.pos)),
466 pos = bookmark.pos,
467 path = parsePath(bookmark.filepath),
468 version = 2
469 }
470 table.insert(newmarks, newmark)
471 end
472 doReplace = true
473 doSave = true
474 end
475 end
476
477 if doEject then
478 for i = #ejects, 1, -1 do table.remove(bookmarks, ejects[i]) end
479 doSave = true
480 end
481
482 if doReplace then bookmarks = newmarks end
483 if doSave then saveBookmarks() end
484
485 if #bookmarks > 0 and currentSlot == 0 then currentSlot = 1 end
486 calcPages()
487end
488
489-- Save the globally loaded bookmarks to the JSON file
490function saveBookmarks()
491 saveTable(bookmarks, getFilepath(bookmarkerName))
492end
493
494-- Make a bookmark of the current media file, position and name
495-- Name can be specified or left blank to automake a name
496-- Returns the bookmark if successful or nil if it can't make a bookmark
497function makeBookmark(bname)
498 if mp.get_property("path") ~= nil then
499 if bname == nil then bname = mp.get_property("media-title").." @ %t" end
500 local bookmark = {
501 name = parseName(bname),
502 pos = mp.get_property_number("time-pos"),
503 path = parsePath(mp.get_property("path")),
504 version = 2
505 }
506 return bookmark
507 else
508 return nil
509 end
510end
511
512-- Add the current position as a bookmark to the global table and then saves it
513-- Returns the slot of the newly added bookmark
514-- Returns -1 if there's an error
515function addBookmark(name)
516 local bookmark = makeBookmark(name)
517 if bookmark == nil then
518 abort(styleOn.."{\\c&H0000FF&}{\\b1}Can't find the media file to create the bookmark for")
519 return -1
520 end
521 table.insert(bookmarks, bookmark)
522
523 if #bookmarks == 1 then currentSlot = 1 end
524
525 calcPages()
526 saveBookmarks()
527 displayBookmarks()
528 return #bookmarks
529end
530
531-- Edit a property of a bookmark at the specified slot
532-- Returns -1 if there's an error
533function editBookmark(slot, property, value)
534 if bookmarkExists(slot) then
535 if property == "name" then value = parseName(value) end
536 bookmarks[slot][property] = value
537 saveBookmarks()
538 else
539 abort(styleOn.."{\\c&H0000FF&}{\\b1}Can't find the bookmark at slot "..slot)
540 return -1
541 end
542end
543
544-- Replaces the bookmark at the specified slot with a provided bookmark
545-- Keeps the name and its position in the list
546-- If the slot is not specified, picks the currently selected bookmark to replace
547-- If a bookmark is not provided, generates a new bookmark
548function replaceBookmark(slot)
549 if slot == nil then slot = currentSlot end
550 if bookmarkExists(slot) then
551 local bookmark = makeBookmark(bookmarks[slot]["name"])
552 if bookmark == nil then
553 abort(styleOn.."{\\c&H0000FF&}{\\b1}Can't find the media file to create the bookmark for")
554 return -1
555 end
556 bookmarks[slot] = bookmark
557 saveBookmarks()
558 if closeAfterReplace then
559 abort(styleOn.."{\\c&H00FF00&}{\\b1}Successfully replaced bookmark:{\\r}\n"..displayName(bookmark["name"]))
560 return -1
561 end
562 return 1
563 else
564 abort(styleOn.."{\\c&H0000FF&}{\\b1}Can't find the bookmark at slot "..slot)
565 return -1
566 end
567end
568
569-- Quickly saves a bookmark without bringing up the menu
570function quickSave()
571 if not active then
572 loadBookmarks()
573 local slot = addBookmark()
574 if slot > 0 then mp.osd_message("Saved new bookmark at slot " .. slot) end
575 end
576end
577
578-- Quickly loads the last bookmark without bringing up the menu
579function quickLoad()
580 if not active then
581 loadBookmarks()
582 local slot = #bookmarks
583 if slot > 0 then mp.osd_message("Loaded bookmark at slot " .. slot) end
584 jumpToBookmark(slot)
585 end
586end
587
588-- Deletes the bookmark in the specified slot from the global table and then saves it
589function deleteBookmark(slot)
590 table.remove(bookmarks, slot)
591 if currentSlot > #bookmarks then currentSlot = #bookmarks end
592
593 calcPages()
594 saveBookmarks()
595 displayBookmarks()
596end
597
598-- Jump to the specified bookmark
599-- This means loading it, reading it, and jumping to the file + position in the bookmark
600function jumpToBookmark(slot)
601 if bookmarkExists(slot) then
602 local bookmark = bookmarks[slot]
603 if fileExists(bookmark["path"]) then
604 if parsePath(mp.get_property("path")) == bookmark["path"] then
605 mp.set_property_number("time-pos", bookmark["pos"])
606 else
607 mp.commandv("loadfile", parsePath(bookmark["path"]), "replace", "start="..bookmark["pos"])
608 end
609 if closeAfterLoad then abort(styleOn.."{\\c&H00FF00&}{\\b1}Successfully found file for bookmark:{\\r}\n"..displayName(bookmark["name"])) end
610 else
611 abort(styleOn.."{\\c&H0000FF&}{\\b1}Can't find file for bookmark:\n" .. displayName(bookmark["name"]))
612 end
613 else
614 abort(styleOn.."{\\c&H0000FF&}{\\b1}Can't find the bookmark at slot " .. slot)
615 end
616end
617
618-- Displays the current page of bookmarks
619function displayBookmarks()
620 -- Determine which slot is the first and last on the current page
621 local startSlot = getFirstSlotOnPage(currentPage)
622 local endSlot = getLastSlotOnPage(currentPage)
623
624 -- Prepare the text to display and display it
625 local display = styleOn .. "{\\b1}Bookmarks page " .. currentPage .. "/" .. maxPage .. ":{\\b0}"
626 for i = startSlot, endSlot do
627 local btext = displayName(bookmarks[i]["name"])
628 local selection = ""
629 if i == currentSlot then
630 selection = "{\\b1}{\\c&H00FFFF&}>"
631 if mode == "move" then btext = "----------------" end
632 btext = btext
633 end
634 display = display .. "\n" .. selection .. i .. ": " .. btext .. "{\\r}"
635 end
636 mp.osd_message(display, rate)
637end
638
639local timer = mp.add_periodic_timer(rate * 0.95, displayBookmarks)
640timer:kill()
641
642-- Commits the message entered with the Typer with custom scripts preceding it
643-- Should typically end with typerExit()
644function typerCommit()
645 local status = 0
646 if mode == "save" then
647 status = addBookmark(typerText)
648 elseif mode == "replace" and typerText == "y" then
649 status = replaceBookmark(currentSlot, makeBookmark(bookmarks[currentSlot]["name"]))
650 elseif mode == "delete" and typerText == "y" then
651 deleteBookmark(currentSlot)
652 elseif mode == "rename" then
653 editBookmark(currentSlot, "name", typerText)
654 elseif mode == "filepath" then
655 editBookmark(currentSlot, "path", typerText)
656 end
657 if status >= 0 then typerExit() end
658end
659
660-- Exits the Typer without committing with custom scripts preceding it
661function typerExit()
662 deactivateTyper()
663 displayBookmarks()
664 timer:resume()
665 mode = "none"
666 activateControls("bookmarker", bookmarkerControls, bookmarkerFlags)
667end
668
669-- Starts the Typer with custom scripts preceding it
670function typerStart()
671 if (mode == "save" or mode=="replace") and mp.get_property("path") == nil then
672 abort(styleOn.."{\\c&H0000FF&}{\\b1}Can't find the media file to create the bookmark for")
673 return -1
674 end
675 if (mode == "replace" or mode == "rename" or mode == "filepath" or mode == "delete") and not bookmarkExists(currentSlot) then
676 abort(styleOn.."{\\c&H0000FF&}{\\b1}Can't find the bookmark at slot "..currentSlot)
677 return -1
678 end
679 if (mode == "replace" and not confirmReplace) or (mode == "delete" and not confirmDelete) then
680 typerText = "y"
681 typerCommit()
682 return
683 end
684
685 deactivateControls("bookmarker", bookmarkerControls)
686 timer:kill()
687 activateTyper()
688 if mode == "rename" then typerText = bookmarks[currentSlot]["name"] end
689 if mode == "filepath" then typerText = bookmarks[currentSlot]["path"] end
690 typerPos = typerText:len()
691 typer("")
692end
693
694-- Aborts the program with an optional error message
695function abort(message)
696 mode = "none"
697 moverExit(true)
698 deactivateTyper()
699 deactivateControls("bookmarker", bookmarkerControls)
700 timer:kill()
701 mp.osd_message(message)
702 active = false
703end
704
705-- Handles the state of the bookmarker
706function handler()
707 if active then
708 abort("")
709 else
710 activateControls("bookmarker", bookmarkerControls, bookmarkerFlags)
711 loadBookmarks()
712 displayBookmarks()
713 timer:resume()
714 active = true
715 end
716end
717
718mp.register_script_message("bookmarker-menu", handler)
719mp.register_script_message("bookmarker-quick-save", quickSave)
720mp.register_script_message("bookmarker-quick-load", quickLoad)