-- Generated by node-lua-distiller(version: 0.2.3)  at Tue Jun 30 2020 22:04:04 GMT+0800 (China Standard Time)

---------------------------------------


if __DISTILLER == nil then
  __DISTILLER = nil
  __DISTILLER = {
    FACTORIES = { },
    __nativeRequire = require,
    require = function(id)
      assert(type(id) == "string", "require invalid id:" .. tostring(id))
      if package.loaded[id] then
        return package.loaded[id]
      end
      if __DISTILLER.FACTORIES[id] then
        local func = __DISTILLER.FACTORIES[id]
        package.loaded[id] = func(__DISTILLER.require) or true
        return package.loaded[id]
      end
      return __DISTILLER.__nativeRequire(id)
    end,
    define = function(self, id, factory)
      assert(type(id) == "string", "invalid id:" .. tostring(id))
      assert(type(factory) == "function", "invalid factory:" .. tostring(factory))
      if package.loaded[id] == nil and self.FACTORIES[id] == nil then
        self.FACTORIES[id] = factory
      else
        print("[__DISTILLER::define] module " .. tostring(id) .. " is already defined")
      end
    end,
    exec = function(self, id)
      local func = self.FACTORIES[id]
      assert(func, "missing factory method for id " .. tostring(id))
      func(__DISTILLER.require)
    end
  }
end



---------------------------------------


__DISTILLER:define("modules.MicroUtils", function(require)
--[[
    MICROUTILS.LUA (MODULE)

    Version:     1.2.0
    Original:    VideoPlayerCode (Javascipt)
    Author:      AssrtOSS
    URL:         https://github.com/VideoPlayerCode/mpv-tools
    License:     Apache License, Version 2.0
]] --

-- luacheck: globals mp

local utils = require("mp.utils")

local Utils = {}

-- https://stackoverflow.com/a/16257287
local function luav()
  -- luacheck: ignore
  local n = "8"
  repeat
    n = n * n
  until n == n * n
  local t = {
    "Lua 5.1",
    nil,
    [-1 / 0] = "Lua 5.2",
    [1 / 0] = "Lua 5.3",
    [2] = "LuaJIT"
  }
  return t[2] or t[#"\z"] or t[n / "-0"] or "Lua 5.4"
end

Utils.luaversion = luav()

-- NOTE: This is an implementation of a non-recursive quicksort, with options
-- of with or without considering case
Utils.quickSort = function(arr, options)
  local tbl = {}

  for i, a in ipairs(arr) do
    tbl[i] = a
  end

  if options and options.caseInsensitive then
    table.sort(
      tbl,
      function(a, b)
        return a:lower() < b:lower()
      end
    )
  end

  table.sort(tbl)
  return tbl
end

Utils.isInt = function(n)
  -- Verify that the input is an integer (whole number).
  return n and tonumber(n) and n - math.floor(n) <= 0
end

local hexSymbols = {
  "0",
  "1",
  "2",
  "3",
  "4",
  "5",
  "6",
  "7",
  "8",
  "9",
  "a",
  "b",
  "c",
  "d",
  "e",
  "f"
}

-- http://lua-users.org/wiki/BitUtils
local function nand(x, y, z)
  z = z or 2 ^ 16
  if z < 2 then
    return 1 - x * y
  else
    return nand((x - x % z) / z, (y - y % z) / z, math.sqrt(z)) * z + nand(x % z, y % z, math.sqrt(z))
  end
end

local bnot = function(y, z)
  return nand(nand(0, 0, z), y, z)
end

local bor = function(x, y, z)
  return nand(bnot(x, z), bnot(y, z), z)
end

local band = function(x, y, z)
  return nand(bnot(0, z), nand(x, y, z), z)
end

-- http://luaforge.net/projects/bit
local function rshift(n, bits)
  if not Utils.isInt(n) then
    error("first operand is not interger: got " .. tostring(n))
  end

  local high_bit = 0
  if (n < 0) then
    -- negative
    n = bnot(math.abs(n)) + 1
    high_bit = 2147483648 -- 0x80000000
  end

  for _ = 1, bits do
    n = n / 2
    n = bor(math.floor(n), high_bit)
  end
  return math.floor(n)
end

local bitAnd = Utils.luaversion == "LuaJIT" and require("bit").band or band

local bitRshift = Utils.luaversion == "LuaJIT" and require("bit").rshift or rshift

Utils.toHex = function(num, outputLength)
  -- Generates a fixed-length output, and handles negative numbers properly.
  local result = ""
  while outputLength > 0 do
    outputLength = outputLength - 1
    result = hexSymbols[bitAnd(num, 0xF) + 1] .. result
    num = bitRshift(num, 4)
  end
  return result
end

Utils.shuffle = function(arr)
  local m = #arr
  local tmp, i

  while m > 0 do -- While items remain to shuffle...
    -- Pick a remaining element...
    i = math.random(0, m - 1) + 1

    -- And swap it with the current element.
    tmp = arr[m]
    arr[m] = arr[i]
    arr[i] = tmp
    m = m - 1
  end

  return arr
end

-- http://lua-users.org/wiki/CommonFunctions
Utils.trim = function(str)
  -- Trim left and right whitespace.
  return (str:gsub("^%s*(.-)%s*$", "%1"))
end

Utils.ltrim = function(str)
  -- Trim left whitespace.
  return (str:gsub("^%s*", ""))
end

Utils.rtrim = function(str)
  -- Trim right whitespace.
  local n = #str
  while n > 0 and str:find("^%s", n) do
    n = n - 1
  end
  return str:sub(1, n)
end

Utils.dump = function(value)
  mp.msg.error(utils.format_json(value))
end

Utils.benchmarkStart = function(textLabel)
  Utils.benchmarkTimestamp = mp.get_time()
  Utils.benchmarkTextLabel = textLabel
end

Utils.benchmarkEnd = function()
  local now = mp.get_time()
  local start = Utils.benchmarkTimestamp or now
  local elapsed = now - start
  local label = Utils.benchmarkTextLabel or ""
  mp.msg.info("Time Elapsed (Benchmark" .. (label and (": " .. label) or "") .. "): " .. elapsed .. " seconds.")
end

return Utils

end)



---------------------------------------


__DISTILLER:define("modules.AssFormat", function(require)
--[[
    ASSFORMAT.LUA (MODULE)

    Version:     1.2.0
    Original:    VideoPlayerCode (Javascipt)
    Author:      AssrtOSS
    URL:         https://github.com/VideoPlayerCode/mpv-tools
    License:     Apache License, Version 2.0
]] --

-- luacheck: globals mp

local Utils = require("modules.MicroUtils")

local Ass = {}

Ass._startSeq = mp.get_property_osd("osd-ass-cc/0")

Ass._stopSeq = mp.get_property_osd("osd-ass-cc/1")

Ass.startSeq = function(output)
  return output == false and "" or Ass._startSeq
end

Ass.stopSeq = function(output)
  return output == false and "" or Ass._stopSeq
end

Ass.esc = function(str, escape)
  if escape == false then -- Conveniently disable escaping via the same call.
    return str
  end
  -- Uses the same technique as mangle_ass() in mpv's osd_libass.c:
  -- - Treat backslashes as literal by inserting a U+2060 WORD JOINER after
  --   them so libass can't interpret the next char as an escape sequence.
  -- - Replace `{` with `\{` to avoid opening an ASS override block. There is
  --   no need to escape the `}` since it's printed literally when orphaned.
  -- - See: https://github.com/libass/libass/issues/194#issuecomment-351902555
  -- \u2060 -> '\xe2\x81\xa0'
  return str:gsub("\\", "\\\226\129\160"):gsub("{", "\\{")
end

Ass.size = function(fontSize, output)
  return output == false and "" or "{\\fs" .. fontSize .. "}"
end

Ass.scale = function(scalePercent, output)
  return output == false and "" or "{\\fscx" .. scalePercent .. "\\fscy" .. scalePercent .. "}"
end

Ass.convertPercentToHex = function(percent, invertValue)
  -- Tip: Use with "invertValue" to convert input range 0.0 (invisible) - 1.0
  -- (fully visible) to hex range '00' (fully visible) - 'FF' (invisible), for
  -- use with the alpha() function in a logical manner for end-users.
  if tonumber(percent) == nil or percent < 0 or percent > 1 then
    error("Invalid percentage value (must be 0.0 - 1.0)")
  end
  return Utils.toHex(
    -- Invert range (optionally), and make into a 0-255 value.
    math.floor(255 * (invertValue and 1 - percent or percent)),
    -- Fixed-size: 2 bytes (00-FF), as needed for hex in ASS subtitles.
    2
  )
end

Ass.alpha = function(transparencyHex, output)
  return output == false and "" or "{\\alpha&H" .. transparencyHex .. "&}" -- 00-FF.
end

Ass.color = function(rgbHex, output)
  return output == false and "" or "{\\1c&H" .. rgbHex:sub(5, 6) .. rgbHex:sub(3, 4) .. rgbHex:sub(1, 2) .. "&}"
end

Ass.white = function(output)
  return Ass.color("FFFFFF", output)
end

Ass.gray = function(output)
  return Ass.color("909090", output)
end

Ass.yellow = function(output)
  return Ass.color("FFFF90", output)
end

Ass.green = function(output)
  return Ass.color("90FF90", output)
end

return Ass

end)



---------------------------------------


__DISTILLER:define("modules.SelectionMenu", function(require)
--[[
    SELECTIONMENU.LUA (MODULE)

    Version:     1.2.0
    Original:    VideoPlayerCode (Javascipt)
    Author:      AssrtOSS
    URL:         https://github.com/VideoPlayerCode/mpv-tools
    License:     Apache License, Version 2.0
]] --

-- luacheck: globals mp

local Ass = require("modules.AssFormat")
local Utils = require("modules.MicroUtils")

local utils = require("mp.utils")

local SelectionMenu = {}

SelectionMenu.new = function(settings)
  settings = settings or {}
  local tbl = {}

  tbl.uniqueId = "M" .. tostring(mp.get_time()):gsub("%.", ""):sub(4) .. tostring(math.random(100, 999))
  tbl.metadata = nil
  tbl.title = "No title"
  tbl.options = {}
  tbl.selectionIdx = 0
  tbl.cbMenuShow = type(settings.cbMenuShow) == "function" and settings.cbMenuShow or nil
  tbl.cbMenuHide = type(settings.cbMenuHide) == "function" and settings.cbMenuHide or nil
  tbl.cbMenuLeft = type(settings.cbMenuLeft) == "function" and settings.cbMenuLeft or nil
  tbl.cbMenuRight = type(settings.cbMenuRight) == "function" and settings.cbMenuRight or nil
  tbl.cbMenuOpen = type(settings.cbMenuOpen) == "function" and settings.cbMenuOpen or nil
  tbl.cbMenuUndo = type(settings.cbMenuUndo) == "function" and settings.cbMenuUndo or nil
  tbl.maxLines = type(settings.maxLines) == "number" and settings.maxLines >= 3 and math.floor(settings.maxLines) or 10
  tbl.menuFontAlpha =
    Ass.convertPercentToHex( -- Throws if invalid input.
    ((type(settings.menuFontAlpha) == "number" and settings.menuFontAlpha >= 0 and settings.menuFontAlpha <= 1) and
      settings.menuFontAlpha or
      1),
    true -- Invert input range so "1.0" is visible and "0.0" is invisible.
  )
  tbl.menuFontSize =
    type(settings.menuFontSize) == "number" and settings.menuFontSize >= 1 and math.floor(settings.menuFontSize) or 40
  tbl.originalFontSize = nil
  tbl.hasRegisteredKeys = false -- Also means that menu is active/open.
  tbl.useTextColors = true
  tbl.currentMenuText = ""
  tbl.isShowingMessage = false
  tbl.currentMessageText = ""
  tbl.menuInterval = nil
  tbl.stopMessageTimeout = nil
  tbl.autoCloseDelay =
    (type(settings.autoCloseDelay) == "number" and
      settings.autoCloseDelay >= 0) and settings.autoCloseDelay or 5 -- 0 = Off.
  tbl.autoCloseActiveAt = 0
  tbl.keyBindings = {
    -- Default keybindings.
    ["Menu-Up"] = {repeatable = true, keys = {"up"}},
    ["Menu-Down"] = {repeatable = true, keys = {"down"}},
    ["Menu-Up-Fast"] = {repeatable = true, keys = {"shift+up"}},
    ["Menu-Down-Fast"] = {repeatable = true, keys = {"shift+down"}},
    ["Menu-Left"] = {repeatable = true, keys = {"left"}},
    ["Menu-Right"] = {repeatable = false, keys = {"right"}},
    ["Menu-Open"] = {repeatable = false, keys = {"enter"}},
    ["Menu-Undo"] = {repeatable = false, keys = {"bs"}},
    ["Menu-Help"] = {repeatable = false, keys = {"h"}},
    ["Menu-Close"] = {repeatable = false, keys = {"esc"}}
  }

  -- Apply custom rebinding overrides if provided.
  -- Format: `{'Menu-Open':['a','shift+b']}`
  -- Note that all "shift variants" MUST be specified as "shift+<key>".
  local rebinds = settings.keyRebindings
  if rebinds then
    for action, allKeys in pairs(rebinds) do
      local erasedDefaults = false
      for i = 1, #allKeys do
        local key = allKeys[i]
        if type(key) == "string" then
          error("Invalid non-string key (" .. utils.format_json(key) .. ") in custom rebindings")
        end
        key = key:lower() -- Unify case of all keys for de-dupe.
        key = Utils.trim(key) -- Trim whitespace.
        if key:len() > 0 then
          if not erasedDefaults then -- Erase default keys for tbl action.
            erasedDefaults = true
            tbl.keyBindings[action].keys = {}
          end
        end
        table.insert(tbl.keyBindings[action].keys, key)
      end
    end
  end

  -- Verify that no duplicate bindings exist for the same key.
  local boundKeys = {}
  for action in pairs(tbl.keyBindings) do
    local allKeys = tbl.keyBindings[action].keys
    for i = 1, #allKeys do
      local key = allKeys[i]
      if boundKeys[key] then
        error('Invalid duplicate menu bindings for key "' .. key .. '" (detected in action "' .. action .. '")')
      end
      boundKeys[key] = true
    end
  end

  return setmetatable(tbl, {__index = SelectionMenu})
end

function SelectionMenu:setMetadata(metadata)
  self.metadata = metadata
end

function SelectionMenu:getMetadata()
  return self.metadata
end

function SelectionMenu:setTitle(newTitle)
  if type(newTitle) ~= "string" then
    error("setTitle: No title value provided")
  end
  self.title = newTitle
end

function SelectionMenu:setOptions(newOptions, initialSelectionIdx)
  if type(newOptions) == "undefined" then
    error("setOptions: No options value provided")
  end
  self.options = newOptions
  self.selectionIdx =
    (type(initialSelectionIdx) == "number" and initialSelectionIdx >= 0 and initialSelectionIdx < #newOptions) and
    initialSelectionIdx or
    0
end

function SelectionMenu:setCallbackMenuShow(newCbMenuShow)
  self.cbMenuShow = type(newCbMenuShow) == "function" and newCbMenuShow or nil
end

function SelectionMenu:setCallbackMenuHide(newCbMenuHide)
  self.cbMenuHide = type(newCbMenuHide) == "function" and newCbMenuHide or nil
end

function SelectionMenu:setCallbackMenuLeft(newCbMenuLeft)
  self.cbMenuLeft = type(newCbMenuLeft) == "function" and newCbMenuLeft or nil
end

function SelectionMenu:setCallbackMenuRight(newCbMenuRight)
  self.cbMenuRight = type(newCbMenuRight) == "function" and newCbMenuRight or nil
end

function SelectionMenu:setCallbackMenuOpen(newCbMenuOpen)
  self.cbMenuOpen = type(newCbMenuOpen) == "function" and newCbMenuOpen or nil
end

function SelectionMenu:setCallbackMenuUndo(newCbMenuUndo)
  self.cbMenuUndo = type(newCbMenuUndo) == "function" and newCbMenuUndo or nil
end

function SelectionMenu:setUseTextColors(value)
  local hasChanged = self.useTextColors ~= value
  self.useTextColors = value ~= nil
  -- Update text cache, and redraw menu if visible (otherwise don't show it).
  if hasChanged then
    self:renderMenu(nil, 1) -- 1 = Only redraw if menu is onscreen.
  end
end

function SelectionMenu:isMenuActive()
  return self.hasRegisteredKeys -- If keys are registered, menu is active.
end

function SelectionMenu:getSelectedItem()
  if self.selectionIdx < 0 or self.selectionIdx >= #self.options then
    return ""
  else
    return self.options[self.selectionIdx + 1]
  end
end

function SelectionMenu:_processBindings(fnCb)
  if type(fnCb) ~= "function" then
    error("Missing callback for _processBindings")
  end

  local bindings = self.keyBindings

  for action in pairs(bindings) do
    local allKeys = bindings[action].keys
    for i = 1, #allKeys do
      local key = allKeys[i]
      local identifier = self.uniqueId .. "_" .. action .. "_" .. key
      fnCb(
        identifier, -- Unique identifier for this binding.
        action, -- What action the key is assigned to trigger.
        key, -- What key.
        bindings[action] -- Details about this binding.
      )
    end
  end
end

function SelectionMenu:_registerMenuKeys()
  if self.hasRegisteredKeys then
    return
  end

  -- Necessary in order to preserve "this" in the called function, since mpv's
  -- callbacks don't receive "this" if the object's func is keybound directly.
  local createFn = function(obj, fn)
    return function()
      obj:_menuAction(fn)
    end
  end

  self:_processBindings(
    function(identifier, action, key, details)
      mp.add_forced_key_binding(
        key, -- What key.
        identifier, -- Unique identifier for the binding.
        createFn(self, action), -- Generate anonymous func to execute.
        {repeatable = details.repeatable} -- Extra options.
      )
    end
  )

  self.hasRegisteredKeys = true
end

function SelectionMenu:_unregisterMenuKeys()
  if not self.hasRegisteredKeys then
    return
  end

  self:_processBindings(
    function(identifier, _, _, _)
      mp.remove_key_binding(
        identifier -- Remove binding by its unique identifier.
      )
    end
  )

  self.hasRegisteredKeys = false
end

function SelectionMenu:_menuAction(action)
  if self.isShowingMessage and action ~= "Menu-Close" then
    return -- Block everything except "close" while showing a message.
  end

  if action == "Menu-Up" or action == "Menu-Down" or action == "Menu-Up-Fast" or action == "Menu-Down-Fast" then
    local maxIdx = #self.options - 1

    if action == "Menu-Up" or action == "Menu-Up-Fast" then
      self.selectionIdx = self.selectionIdx - (action == "Menu-Up-Fast" and 10 or 1)
    else
      self.selectionIdx = self.selectionIdx + (action == "Menu-Down-Fast" and 10 or 1)
    end

    -- Handle wraparound in single-move mode, or clamp in fast-move mode.
    if self.selectionIdx < 0 then
      self.selectionIdx = (action == "Menu-Up-Fast" and 0 or maxIdx)
    elseif self.selectionIdx > maxIdx then
      self.selectionIdx = (action == "Menu-Down-Fast" and maxIdx or 0)
    end

    self:renderMenu()
  elseif action == "Menu-Left" or action == "Menu-Right" or action == "Menu-Open" or action == "Menu-Undo" then
    local cbName = "cb" .. action:gsub("-", "")
    if type(self[cbName]) == "function" then
      -- We don't know what the callback will do, and it may be slow, so
      -- we'll disable the menu's auto-close timeout while it runs.
      self:_disableAutoCloseTimeout() -- Soft-disable.
      self[cbName](action)
    end
  elseif action == "Menu-Help" then
    -- List all keybindings to help the user remember them.
    local entryTitle, allKeys
    local c = self.useTextColors
    local helpLines = 0
    local helpString = Ass.startSeq(c) .. Ass.alpha(self.menuFontAlpha, c)
    local bindings = self.keyBindings
    for entry in pairs(bindings) do
      allKeys = bindings[entry].keys
      if not (entry:match("^Menu-") or not allKeys or #allKeys == 0) then
        entryTitle = entry:sub(5)
        if entryTitle:len() > 0 then
          Utils.quickSort(allKeys, {caseInsensitive = true})
          helpLines = helpLines + 1
          helpString =
            helpString ..
            Ass.yellow(c) ..
              Ass.esc(entryTitle, c) .. ": " .. Ass.white(c) .. Ass.esc("{" .. allKeys.join("}, {") .. "}", c) .. "\n"
        end
      end
    end
    helpString = helpString .. Ass.stopSeq(c)
    if not helpLines then
      helpString = "No help available."
    end
    self:showMessage(helpString, 5000)
  elseif action == "Menu-Close" then
    self:hideMenu()
  else
    mp.msg.error('Unknown menu action "' .. action .. '"')
    return
  end

  self:_updateAutoCloseTimeout() -- Soft-update.
end

function SelectionMenu:_disableAutoCloseTimeout(forceLock)
  self.autoCloseActiveAt = forceLock and -2 or -1 -- -2 = hard, -1 = soft.
end

function SelectionMenu:_updateAutoCloseTimeout(forceUnlock)
  if not forceUnlock and self.autoCloseActiveAt == -2 then
    -- Do nothing while autoclose is locked in "disabled" mode.
    return
  end

  self.autoCloseActiveAt = mp.get_time()
end

function SelectionMenu:_handleAutoClose()
  if self.autoCloseDelay <= 0 or self.autoCloseActiveAt <= -1 then
    -- -2 = hard, -1 = soft.
    -- Do nothing while autoclose is disabled (0) or locked (< 0).
    return
  end

  local now = mp.get_time()
  if self.autoCloseActiveAt <= (now - self.autoCloseDelay) then
    self:hideMenu()
  end
end

function SelectionMenu:_renderActiveText()
  if not self:isMenuActive() then
    return
  end

  -- Determine which text to render (critical messages take precedence).
  local msg = self.isShowingMessage and self.currentMessageText or self.currentMenuText
  if type(msg) ~= "string" then
    msg = ""
  end

  -- Tell mpv's OSD to show the text. It will automatically be replaced and
  -- refreshed every second while the menu remains open, to ensure that
  -- nothing else is able to overwrite our menu text.
  -- NOTE: The long display duration is important, because the JS engine lacks
  -- real threading, so any slow mpv API calls or slow JS functions will delay
  -- our redraw timer! Without a long display duration, the menu would vanish.
  -- NOTE: If a timer misses multiple intended ticks, it will only tick ONCE
  -- when catching up. So there can thankfully never be any large "backlog"!
  mp.osd_message(msg, 1000)
end

function SelectionMenu:renderMenu(selectionPrefix, renderMode)
  local c = self.useTextColors
  local finalString

  -- Title.
  finalString =
    Ass.startSeq(c) ..
    Ass.alpha(self.menuFontAlpha, c) ..
      Ass.gray(c) .. Ass.scale(75, c) .. Ass.esc(self.title, c) .. ":" .. Ass.scale(100, c) .. Ass.white(c) .. "\n\n"

  -- Options.
  if #self.options > 0 then
    -- Calculate start/end offsets around focal point.
    local startIdx = self.selectionIdx - math.floor(self.maxLines / 2)
    if startIdx < 0 then
      startIdx = 0
    end

    local endIdx = startIdx + self.maxLines - 1
    local maxIdx = #self.options - 1
    if endIdx > maxIdx then
      endIdx = maxIdx
    end

    -- Increase number of leading lines if we've reached end of list.
    local lineCount = (endIdx - startIdx) + 1 -- "+1" to count start line too.
    local lineDiff = self.maxLines - lineCount
    startIdx = startIdx - lineDiff
    if startIdx < 0 then
      startIdx = 0
    end

    -- Format and add all output lines.
    local opt
    for i = startIdx, endIdx do
      opt = self.options[i + 1]
      if i == self.selectionIdx then
        -- NOTE: Prefix stays on screen until cursor-move or re-render.
        finalString =
          finalString ..
          Ass.yellow(c) .. "> " .. (type(selectionPrefix) == "string" and (Ass.esc(selectionPrefix, c) .. " ") or "")
      end
      finalString =
        finalString ..
        ((i == startIdx and startIdx > 0) and "..." or
          ((i == endIdx and endIdx < maxIdx) and "..." or Ass.esc(type(opt) == "object" and opt.menuText or opt, c)))
      if i == self.selectionIdx then
        finalString = finalString .. Ass.white(c)
      end
      if i ~= endIdx then
        finalString = finalString .. "\n"
      end
    end
  end

  -- End the Advanced SubStation command sequence.
  finalString = finalString .. Ass.stopSeq(c)

  -- Update cached menu text. But only open/redraw the menu if it's already
  -- active OR if we're NOT being prevented from going out of "hidden" state.
  self.currentMenuText = finalString

  -- Handle render mode:
  -- 1 = Only redraw if menu is onscreen (doesn't trigger open/redrawing if
  -- the menu is closed or busy showing a text message) 2 = Don't show/redraw
  -- at all (good for just updating the text cache silently) any other value
  -- (incl. undefined, aka default) = show/redraw the menu.
  if (renderMode == 1 and (not self:isMenuActive() or self.isShowingMessage)) or renderMode == 2 then
    return
  end
  self:_showMenu()
end

function SelectionMenu:_showMenu()
  local justOpened = false
  if not self:isMenuActive() then
    justOpened = true
    self.originalFontSize = mp.get_property_number("osd-font-size")
    mp.set_property("osd-font-size", self.menuFontSize)
    self:_registerMenuKeys()

    -- Redraw the currently active text every second and do periodic tasks.
    -- NOTE: This prevents other OSD scripts from removing our menu text.
    if self.menuInterval ~= nil then
      self.menuInterval:stop()
    end
    self.menuInterval =
      mp.add_periodic_timer(
      1,
      function()
        self:_renderActiveText()
        self:_handleAutoClose()
      end
    )

    -- Get rid of any lingering "stop message" timeout and message.
    self:stopMessage(true)
  end

  -- Display the currently active text instantly.
  self:_renderActiveText()

  if justOpened then
    -- Run "menu show" callback if registered.
    if type(self.cbMenuShow) == "function" then
      self:_disableAutoCloseTimeout() -- Soft-disable while CB runs.
      self:cbMenuShow("Menu-Show")
    end

    -- Force an update/unlock of the activity timeout when menu opens.
    self:_updateAutoCloseTimeout(true) -- Hard-update.
  end
end

function SelectionMenu:hideMenu()
  if not self:isMenuActive() then
    return
  end

  mp.osd_message("")
  if self.originalFontSize ~= nil then
    mp.set_property("osd-font-size", self.originalFontSize)
  end
  self:_unregisterMenuKeys()
  if self.menuInterval ~= nil then
    self.menuInterval:stop()
    self.menuInterval = nil
  end

  -- Get rid of any lingering "stop message" timeout and message.
  self:stopMessage(true)

  -- Run "menu hide" callback if registered.
  if type(self.cbMenuHide) == "function" then
    self:cbMenuHide("Menu-Hide")
  end
end

function SelectionMenu:showMessage(msg, durationMs, clearSelectionPrefix)
  if not self:isMenuActive() then
    return
  end

  if type(msg) ~= "string" then
    msg = "showMessage: Invalid message value."
  end
  if type(durationMs) ~= "number" then
    durationMs = 800
  end

  local duration
  if durationMs < 1000 then
    duration = 1
  else
    duration = durationMs / 1000
  end

  if clearSelectionPrefix then
    self:renderMenu(nil, 2) -- 2 = Only update text cache (no redraw).
  end

  self.isShowingMessage = true
  self.currentMessageText = msg
  self:_renderActiveText()
  self:_disableAutoCloseTimeout(true) -- Hard-disable (ignore msg idle time).

  if self.stopMessageTimeout ~= nil then
    self.stopMessageTimeout:stop()
  end
  self.stopMessageTimeout =
    mp.add_timeout(
    duration,
    function()
      self:stopMessage()
    end
  )
end

function SelectionMenu:stopMessage(preventRender)
  if self.stopMessageTimeout ~= nil then
    self.stopMessageTimeout:stop()
    self.stopMessageTimeout = nil
  end
  self.isShowingMessage = false
  self.currentMessageText = ""
  if not preventRender then
    self:_renderActiveText()
  end
  self:_updateAutoCloseTimeout(true) -- Hard-update (last user activity).
end

return SelectionMenu

end)



---------------------------------------


__DISTILLER:define("assrt.lua_distilled", function(require)
--[[
    assrt.lua

    Description: Search subtitle on assrt.net
    Version:     1.0.2
    Author:      AssrtOpensource
    URL:         https:-- github.com/AssrtOSS/mpv-assrt
    License:     Apache License, Version 2.0
]] --

-- luacheck: globals mp read_options

local read_options = read_options or require("mp.options").read_options
local utils = require("mp.utils")

local script_path = debug.getinfo(1, "S").source:sub(2)
local script_dir = utils.split_path(script_path)

local Ass
do
  local ok
  ok, Ass = pcall(require, "modules.AssFormat")
  if not ok then
    -- try inject current script directory into package.path
    package.path = script_dir .. "?.lua;;" .. package.path
    Ass = require("modules.AssFormat")
  end
end

local SelectionMenu = require("modules.SelectionMenu")

local VERSION = "1.0.6"

local COMMON_PREFIX_KEY = "##common-prefix##"
local RLSITE_KEY = "##release-site##"

local ASSRT = {}

local tmpDir

local function getTmpDir()
  if not tmpDir then
    local temp = os.getenv("TEMP") or os.getenv("TMP") or os.getenv("TMPDIR")
    if temp then
      tmpDir = temp
    else
      tmpDir = "/tmp"
    end
  end
  return tmpDir
end

local function fileExists(path)
  if utils.file_info then -- >= 0.28.0
      return utils.file_info(path)
  end
  local ok, _ = pcall(io.open, path)
  return ok
end

local function testDownloadTool()
  local _UA = mp.get_property("mpv-version"):gsub(" ", "/") .. " assrt-" .. VERSION
  local UA = "User-Agent: " .. _UA
  local cmds = {
    {"curl", "-SLs", "-H", UA},
    {"wget", "-q", "--header", UA, "-O", "-"},
    {
      "powershell",
      ' Invoke-WebRequest -UserAgent "' .. _UA .. '"  -ContentType "application/json charset=utf-8" -URI '
    }
  }
  local _winhelper = script_dir .. "win-helper.vbs"
  if fileExists(_winhelper) then
    table.insert(cmds, { "cscript", "/nologo", _winhelper, _UA })
  end
  for i = 1, #cmds do
    local result =
      utils.subprocess(
      {
        args = {cmds[i][1], "-h"},
        cancellable = false
      }
    )
    if type(result.stdout) == "string" and result.status ~= -1 then
      mp.msg.info("selected: ", cmds[i][1])
      return cmds[i]
    end
  end
  return
end

local function httpget(args, url, saveFile)
  local tbl = {}
  for i, arg in ipairs(args) do
    tbl[i] = arg
  end
  args = tbl

  local isSaveFile = (saveFile ~= nil)
  saveFile = saveFile or utils.join_path(getTmpDir(), ".assrt-helper.tmp")

  if args[1] == "powershell" then
    args[#args.length] = args[#args.length] .. '"' .. url .. '" -Outfile "' .. saveFile .. '"'
  else
    if args[1] == "cscript" then
      table.insert(args, url, saveFile)
    else
      if isSaveFile then
        if args[1] == "wget" then
          args[#args] = nil -- pop "-"
        else
          table.insert(args, "-o")
        end
        table.insert(args, saveFile)
      end
      table.insert(args, url)
    end

    local result =
      utils.subprocess(
      {
        args = args,
        cancellable = true
      }
    )

    if result.stderr or result.status ~= 0 then
      mp.msg.error(result.stderr or ("subprocess exit with code " .. result.status))
      return
    end

    if isSaveFile then
      -- TODO: check file sanity
      return true
    else
      if args[1] == "powershell" or args[1] == "cscript" then
        return utils.read_file(saveFile)
      else
        return result.stdout
      end
    end
  end
end

function ASSRT.new(options)
  options = options or {}
  local tbl = {}

  tbl.cmd = nil
  tbl.apiToken = options.apiToken
  tbl.useHttps = options.useHttps
  tbl.autoRename = options.autoRename

  tbl._list_map = {}
  tbl._enableColor = mp.get_property_bool("vo-configured") or true
  tbl._menu_state = {}

  tbl.menu =
    SelectionMenu.new(
    {
      maxLines = options.maxLines,
      menuFontSize = options.menuFontSize,
      autoCloseDelay = options.autoCloseDelay,
      keyRebindings = options.keyRebindings
    }
  )
  tbl.menu:setMetadata({type = nil})
  tbl.menu:setUseTextColors(tbl._enableColor)

  -- callbacks
  local _open = function()
    table.insert(
      tbl._menu_state,
      {
        type = tbl.menu:getMetadata().type,
        options = tbl.menu.options,
        list_map = tbl._list_map,
        title = tbl.menu.title,
        idx = tbl.menu.selectionIdx,
        ass_esc = Ass.esc
      }
    )

    local selectedItem = tbl.menu:getSelectedItem()
    tbl.menu:hideMenu()

    local call_map = {
      list = tbl.getSubtitleDetail,
      detail = tbl.downloadSubtitle
    }
    call_map[tbl.menu:getMetadata().type](tbl, selectedItem)
  end
  tbl.menu:setCallbackMenuOpen(_open)
  tbl.menu:setCallbackMenuRight(_open)

  tbl.menu:setCallbackMenuHide(
    function()
      -- restore escape function if needed
      if Ass._old_esc then
        Ass.esc = Ass._old_esc
        Ass._old_esc = nil
      end
    end
  )

  local _undo = function()
    if #tbl._menu_state == 0 then
      tbl.menu:hideMenu()
      return
    end
    local state = tbl._menu_state[#tbl._menu_state]
    tbl._menu_state[#tbl._menu_state] = nil
    tbl._list_map = state.list_map
    Ass.esc = state.ass_esc
    tbl.menu:getMetadata().type = state.type
    tbl.menu:setTitle(state.title)
    tbl.menu:setOptions(state.options, state.idx)
    tbl.menu:renderMenu()
  end
  tbl.menu:setCallbackMenuUndo(_undo)
  tbl.menu:setCallbackMenuLeft(_undo)

  return setmetatable(tbl, {__index = ASSRT})
end

local function _showOsdColor(self, output, duration, color)
  local c = self._enableColor
  local _originalFontSize = mp.get_property_number("osd-font-size")
  mp.set_property("osd-font-size", self.menu.menuFontSize)
  mp.osd_message(
    Ass.startSeq(c) .. Ass.color(color, c) .. Ass.scale(75, c) .. Ass.esc(output, c) .. Ass.stopSeq(c),
    duration
  )
  mp.set_property("osd-font-size", _originalFontSize)
end

function ASSRT:showOsdError(output, duration)
  _showOsdColor(self, output, duration, "FE2424")
end

function ASSRT:showOsdInfo(output, duration)
  _showOsdColor(self, output, duration, "F59D1A")
end

function ASSRT:showOsdOk(output, duration)
  _showOsdColor(self, output, duration, "90FF90")
end

function ASSRT:api(uri, arg)
  if not self.cmd then
    self.cmd = testDownloadTool()
  end
  if not self.cmd then
    mp.msg.error("no wget or curl found")
    self:showOsdError("ASSRT: 没有找到wget和curl，无法运行", 2)
    return
  end

  local url =
    (self.useHttps and "https" or "http") ..
    "://api.assrt.net/v1" .. uri .. "?token=" .. self.apiToken .. "&" .. (arg and arg or "")
  local ret = httpget(self.cmd, url)
  if not ret then
    return
  end

  local err

  ret, err = utils.parse_json(ret)
  if err then
    mp.msg.error(err)
    return
  end
  if ret.status > 0 then
    mp.msg.error("API failed with code: " .. ret.status .. ", message: " .. ret.errmsg)
    return
  end
  return ret
end

local function formatLang(s, output)
  s = Ass._old_esc(s)
  if not output then
    return s
  end
  local color_list = {
    ["英"] = "00247D",
    ["简"] = "f40002",
    ["繁"] = "000098",
    ["双语"] = "ffffff"
  }
  return s:gsub(
    "%S+",
    function(match)
      local c = color_list[match]
      if c then
        return Ass.color(c, true) .. match .. Ass.white(true)
      else
        return Ass.color("8e44ad", true) .. match .. Ass.white(true)
      end
    end
  ) .. Ass.white(true)
end

-- https://github.com/daurnimator/lua-http/blob/master/http/util.lua
local function char_to_pchar(c)
  return string.format("%%%02X", c:byte(1, 1))
end

local function encodeURIComponent(s)
  return s:gsub("[^%w%-_%.%!%~%*%'%(%)]", char_to_pchar)
end

function ASSRT:searchSubtitle()
  self:showOsdInfo("正在搜索字幕...", 2)
  local fpath = mp.get_property("path", " ")
  local _, fname = utils.split_path(fpath)
  local try_args = {"is_file", "no_muxer"}
  local sublist
  fname = fname:gsub("[%(%)~]", "")
  for i = 1, 2 do
    local ret = self:api("/sub/search", "q=" .. encodeURIComponent(fname) .. "&" .. try_args[i] .. "=1")
    if ret and ret.sub.subs then
      if not sublist then
        sublist = {}
      end
      for _, s in ipairs(ret.sub.subs) do
        table.insert(sublist, s)
      end
      if #sublist >= 3 then
        break
      end
    end
  end
  if not sublist then
    if self.cmd then -- don't overlap cmd error
      self:showOsdError("API请求错误，请检查控制台输出", 2)
    end
    return
  end
  if #sublist == 0 then -- ????
    self:showOsdOk("没有符合条件的字幕", 1)
    return
  end

  local menuOptions = {}
  local initialSelectionIdx = 0
  self._list_map = {}

  if not Ass._old_esc then
    Ass._old_esc = Ass.esc
    -- disable escape temporarily
    Ass.esc = function(str, _)
      return str
    end
  end
  local seen = {}
  for i = 1, #sublist do
    local id = sublist[i].id
    if not seen[id] then
      seen[id] = true
      local title = Ass._old_esc(sublist[i].native_name)
      if title == "" then
        title = Ass._old_esc(sublist[i].videoname)
      end
      if sublist[i].release_site ~= nil then
        title =
          Ass.alpha("88", self._enableColor) ..
          (self._enableColor and "" or "[") ..
            Ass._old_esc(sublist[i].release_site) ..
              (self._enableColor and "  " or "]  ") ..
                Ass.alpha("00", self._enableColor) ..
                  Ass.alpha("55", self._enableColor) .. title .. Ass.alpha("00", self._enableColor)
      end
      if sublist[i].lang ~= nil then
        title =
          title ..
          (self._enableColor and "  " or "  [") ..
            formatLang(sublist[i].lang.desc, self._enableColor) .. (self._enableColor and "  " or "]  ")
      end
      table.insert(menuOptions, title)
      self._list_map[title] = id
    end
    -- if (selectEntry == sub)
    --    initialSelectionIdx = menuOptions.length - 1
  end

  self.menu:getMetadata().type = "list"

  self.menu:setTitle("选择字幕")
  self.menu:setOptions(menuOptions, initialSelectionIdx)
  self.menu:renderMenu()
end

-- https://github.com/NemoAlex/glutton/blob/master/src/services/util.js#L32
local function findCommon(names)
  if #names <= 1 then
    return nil
  end
  local name = names[1]
  local common = ''
  for i=2, #name, 1 do
    local test = name:sub(1, i)
    local success = true
    for j=2, #names, 1 do
      if names[j]:sub(1, i) ~= test then
        success = false
        break
      end
    end
    if not success then
      break
    end
    common = test
  end
  return #common
end

local function isExtensionArchive(s)
  for _, p in ipairs({".rar", ".zip", ".7z"}) do
    if s:sub(#s-#p+1) == p then
      return true
    end
  end
  return false
end

function ASSRT:getSubtitleDetail(selection)
  self:showOsdInfo("正在获取字幕详情...", 2)
  local id = self._list_map[selection]

  local ret = self:api("/sub/detail", "id=" .. id)
  if not ret and self.cmd then -- don't overlap cmd error
    self:showOsdError("API请求错误，请检查控制台输出", 2)
    return
  end

  local title
  local menuOptions = {}
  local initialSelectionIdx = 0

  self._list_map = {}

  local filelist = ret.sub.subs[1].filelist
  local fnames = {}
  for i = 1, #filelist do
    title = filelist[i].f
    table.insert(menuOptions, title)
    table.insert(fnames, title)
    self._list_map[title] = filelist[i].url
    -- if (selectEntry == sub)
    --    initialSelectionIdx = menuOptions.length - 1
  end

  self._list_map[COMMON_PREFIX_KEY] = findCommon(fnames)

  local rlsite = ret.sub.subs[1].release_site
  self._list_map[RLSITE_KEY] = rlsite == "个人" and nil or rlsite

  self.menu:getMetadata().type = "detail"

  -- if filelist is empty and file is not archive, go ahead and download
  if (not menuOptions.length or menuOptions.length == 0) and
      not isExtensionArchive(ret.sub.subs[1].filename) then
    title = ret.sub.subs[1].filename
    table.insert(menuOptions, title)
    self._list_map[title] = ret.sub.subs[1].url

    -- download it directly
    return self:downloadSubtitle(title)
  end

  self.menu:setTitle("下载字幕")
  self.menu:setOptions(menuOptions, initialSelectionIdx)
  self.menu:renderMenu()
end

function ASSRT:downloadSubtitle(selection)
  local url = self._list_map[selection]

  self:showOsdInfo("正在下载字幕...", 10)

  local saveFile
  local mediaPath = mp.get_property("path", " ")
  -- use the same directory as mediaPath by default
  local _dir, _ = utils.split_path(mediaPath)
  if mediaPath and mediaPath:match("^%a+://") then
    -- is web, use temp path
    _dir = getTmpDir()
  end
  local fname = selection
  if self.autoRename then
    local mname = mp.get_property("filename/no-ext", " ")
    if mname then
      -- rlsite
      if self._list_map[RLSITE_KEY] then
        mname = mname .. "." .. self._list_map[RLSITE_KEY]
      end
      -- partial without common prefix
      local common_len = self._list_map[COMMON_PREFIX_KEY]
      local suffix
      if common_len then
        suffix = selection:sub(common_len)
      end
      if not suffix then -- nothing left? use extension
        suffix = selection:match("(%.[^\\%.]+)$")
      elseif suffix:sub(1, 1) ~= "." then
        mname = mname .. "."
      end
      fname = mname .. suffix
    end
  end
  saveFile = utils.join_path(_dir, fname)

  local ret = httpget(self.cmd, url, saveFile)

  if not ret then
    self:showOsdError("字幕下载失败，请检查控制台输出", 2)
    return
  end

  self:showOsdOk("字幕已下载", 2)
  mp.commandv("sub-add", saveFile)
end

local function init()
  local userConfig = {
    api_token = "tNjXZUnOJWcHznHDyalNMYqqP6IdDdpQ",
    use_https = true,
    auto_close = 5,
    max_lines = 15,
    font_size = 24,
    auto_rename = true,
  }
  read_options(userConfig, "assrt")

  -- Create and initialize the media browser instance.
  local assrt
  local status, err =
    pcall(
    function()
      assrt =
        ASSRT.new(
        {
          apiToken = userConfig["api_token"],
          useHttps = userConfig["use_https"],
          autoCloseDelay = userConfig["auto_close"],
          maxLines = userConfig["max_lines"],
          menuFontSize = userConfig["font_size"],
          autoRename = userConfig['auto_rename'],
        }
      )
    end
  )
  if not status then
    mp.msg.error("ASSRT: " .. err .. ".")
    mp.osd_message("ASSRT: " .. err .. ".", 3)
    error(err) -- Critical init error. Stop script execution.
  end

  -- Provide the bindable mpv command which opens/cycles through the menu.
  -- Bind self via input.conf: `a script-binding assrt`.
  mp.add_key_binding(
    "a",
    "assrt",
    function()
      assrt:searchSubtitle()
    end
  )
  mp.msg.info("loaded assrt Lua flavor")
end

init()

end)



---------------------------------------


__DISTILLER:exec("assrt.lua_distilled")