Skip to content

Commit

Permalink
add comments to lua code
Browse files Browse the repository at this point in the history
  • Loading branch information
tsl0922 committed Jan 9, 2024
1 parent a9afe4f commit 60edd00
Showing 1 changed file with 56 additions and 47 deletions.
103 changes: 56 additions & 47 deletions lua/dyn_menu.lua
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

local opts = require('mp.options')

-- user options
local o = {
max_title_length = 80, -- limit the title length, set to 0 to disable.
}
Expand All @@ -25,6 +26,7 @@ local menu_prop = 'user-data/menu/items'
local menu_items = mp.get_property_native(menu_prop, {})
local menu_items_dirty = false

-- escape codec name to make it more readable
local function escape_codec(str)
if not str or str == '' then return '' end
if str:find("mpeg2") then return "mpeg2"
Expand All @@ -40,6 +42,7 @@ local function escape_codec(str)
else return str end
end

-- abbreviate title if it's too long
local function abbr_title(str)
if not str or str == '' then return '' end
if o.max_title_length > 0 and str:len() > o.max_title_length then
Expand All @@ -48,23 +51,32 @@ local function abbr_title(str)
return str
end

-- build track title from track metadata
--
-- example:
-- V: Video 1 [h264, 1920x1080, 23.976 fps] (*) JPN
-- | | | | |
-- type title hints default lang
local function build_track_title(track, prefix, filename)
local type = track.type
local title = track.title or ''
local lang = track.lang or ''
local codec = escape_codec(track.codec)

-- remove filename from title if it's external track
if track.external and title ~= '' then
if filename ~= '' then title = title:gsub(filename .. '%.?', '') end
if title:lower() == codec:lower() then title = '' end
end
-- set a default title if it's empty
if title == '' then
local name = type:sub(1, 1):upper() .. type:sub(2, #type)
title = string.format('%s %d', name, track.id)
else
title = abbr_title(title)
end

-- build hints from track metadata
local hints = {}
local function h(value) hints[#hints + 1] = value end
if codec ~= '' then h(codec) end
Expand All @@ -77,23 +89,29 @@ local function build_track_title(track, prefix, filename)
if track['demux-bitrate'] then h(string.format('%.3g kbps', track['demux-bitrate'] / 1000)) end
if #hints > 0 then title = string.format('%s [%s]', title, table.concat(hints, ', ')) end

-- put some important info at the end
if track.forced then title = title .. ' (forced)' end
if track.external then title = title .. ' (external)' end
if track.default then title = title .. ' (*)' end

-- show language at right side (\t is used to right align the text)
if lang ~= '' then title = string.format('%s\t%s', title, lang:upper()) end
-- prepend a 1-letter type prefix, used when displaying multiple track types
if prefix then title = string.format('%s: %s', type:sub(1, 1):upper(), title) end
return title
end

-- build track menu items from track list for given type
local function build_track_items(list, type, prop, prefix)
local items = {}

-- filename without extension, escaped for pattern matching
local filename = mp.get_property('filename/no-ext', ''):gsub("[%(%)%.%%%+%-%*%?%[%]%^%$]", "%%%0")
local pos = mp.get_property_number(prop, -1)
for _, track in ipairs(list) do
if track.type == type then
local state = {}
-- there may be 2 tracks selected at the same time, for example: subtitle
if track.selected then
table.insert(state, 'checked')
if track.id ~= pos then table.insert(state, 'disabled') end
Expand All @@ -107,6 +125,7 @@ local function build_track_items(list, type, prop, prefix)
end
end

-- add an extra item to disable or re-enable the track
if #items > 0 then
local title = pos > 0 and 'Off' or 'Auto'
local value = pos > 0 and 'no' or 'auto'
Expand All @@ -121,41 +140,44 @@ local function build_track_items(list, type, prop, prefix)
return items
end

-- handle #@tracks menu update
local function update_tracks_menu(submenu)
mp.observe_property('track-list', 'native', function(_, track_list)
if not track_list then return end
for i = #submenu, 1, -1 do table.remove(submenu, i) end
menu_items_dirty = true
if not track_list or #track_list == 0 then return end

local items_v = build_track_items(track_list, 'video', 'vid', true)
local items_a = build_track_items(track_list, 'audio', 'aid', true)
local items_s = build_track_items(track_list, 'sub', 'sid', true)

-- append video/audio/sub tracks into one submenu, separated by a separator
for _, item in ipairs(items_v) do table.insert(submenu, item) end
if #submenu > 0 and #items_a > 0 then table.insert(submenu, { type = 'separator' }) end
for _, item in ipairs(items_a) do table.insert(submenu, item) end
if #submenu > 0 and #items_s > 0 then table.insert(submenu, { type = 'separator' }) end
for _, item in ipairs(items_s) do table.insert(submenu, item) end

menu_items_dirty = true
end)
end

-- handle #@tracks/<type> menu update for given type
local function update_track_menu(submenu, type, prop)
mp.observe_property('track-list', 'native', function(_, track_list)
if not track_list then return end
for i = #submenu, 1, -1 do table.remove(submenu, i) end
menu_items_dirty = true
if not track_list or #track_list == 0 then return end

local items = build_track_items(track_list, type, prop, false)
for _, item in ipairs(items) do table.insert(submenu, item) end

menu_items_dirty = true
end)
end

-- handle #@chapters menu update
local function update_chapters_menu(submenu)
mp.observe_property('chapter-list', 'native', function(_, chapter_list)
if not chapter_list then return end
for i = #submenu, 1, -1 do table.remove(submenu, i) end
menu_items_dirty = true
if not chapter_list or #chapter_list == 0 then return end

local pos = mp.get_property_number('chapter', -1)
for id, chapter in ipairs(chapter_list) do
Expand All @@ -169,25 +191,23 @@ local function update_chapters_menu(submenu)
state = id == pos + 1 and { 'checked' } or {},
}
end

menu_items_dirty = true
end)

mp.observe_property('chapter', 'number', function(_, pos)
if not pos then pos = -1 end

for id, item in ipairs(submenu) do
item.state = id == pos + 1 and { 'checked' } or {}
end

menu_items_dirty = true
end)
end

-- handle #@edition menu update
local function update_editions_menu(submenu)
mp.observe_property('edition-list', 'native', function(_, edition_list)
if not edition_list then return end
for i = #submenu, 1, -1 do table.remove(submenu, i) end
menu_items_dirty = true
if not edition_list or #edition_list == 0 then return end

local current = mp.get_property_number('current-edition', -1)
for id, edition in ipairs(edition_list) do
Expand All @@ -200,25 +220,23 @@ local function update_editions_menu(submenu)
state = id == current + 1 and { 'checked' } or {},
}
end

menu_items_dirty = true
end)

mp.observe_property('current-edition', 'number', function(_, pos)
if not pos then pos = -1 end

for id, item in ipairs(submenu) do
item.state = id == pos + 1 and { 'checked' } or {}
end

menu_items_dirty = true
end)
end

-- handle #@audio-devices menu update
local function update_audio_devices_menu(submenu)
mp.observe_property('audio-device-list', 'native', function(_, device_list)
if not device_list then return end
for i = #submenu, 1, -1 do table.remove(submenu, i) end
menu_items_dirty = true
if not device_list or #device_list == 0 then return end

local current = mp.get_property('audio-device', '')
for _, device in ipairs(device_list) do
Expand All @@ -228,25 +246,23 @@ local function update_audio_devices_menu(submenu)
state = device.name == current and { 'checked' } or {},
}
end

menu_items_dirty = true
end)

mp.observe_property('audio-device', 'string', function(_, name)
if not name then name = '' end

for _, item in ipairs(submenu) do
item.state = item.cmd:match('%s*set audio%-device%s+(%S+)%s*$') == name and { 'checked' } or {}
end

menu_items_dirty = true
end)
end

-- handle #@profiles menu update
local function update_profiles_menu(submenu)
mp.observe_property('profile-list', 'native', function(_, profile_list)
if not profile_list then return end
for i = #submenu, 1, -1 do table.remove(submenu, i) end
menu_items_dirty = true
if not profile_list or #profile_list == 0 then return end

for _, profile in ipairs(profile_list) do
if not (profile.name == 'default' or profile.name:find('gui') or
Expand All @@ -257,17 +273,15 @@ local function update_profiles_menu(submenu)
}
end
end

menu_items_dirty = true
end)
end

local file_scope_dyn_menus = {}

-- update dynamic menu item and handle submenu update
local function dyn_menu_update(item, keyword)
item.type = 'submenu'
item.submenu = {}
item.cmd = nil
menu_items_dirty = true

if keyword == 'tracks' then
update_tracks_menu(item.submenu)
Expand All @@ -288,14 +302,14 @@ local function dyn_menu_update(item, keyword)
elseif keyword == 'profiles' then
update_profiles_menu(item.submenu)
end

if keyword ~= 'audio-devices' and keyword ~= 'profiles' then
file_scope_dyn_menus[#file_scope_dyn_menus + 1] = item.submenu
end

menu_items_dirty = true
end

-- find #@keyword for dynamic menu and handle updates
--
-- cplugin will keep the trailing comments in the cmd field, so we can
-- parse the keyword from it.
--
-- example: ignore #menu: Chapters #@chapters # extra comment
local function dyn_menu_check(items)
if not items then return end
for _, item in ipairs(items) do
Expand All @@ -310,38 +324,33 @@ local function dyn_menu_check(items)
end
end

local function dyn_menu_init()
dyn_menu_check(menu_items)

if #file_scope_dyn_menus > 0 then
mp.register_event('end-file', function()
for _, submenu in ipairs(file_scope_dyn_menus) do
for i = #submenu, 1, -1 do table.remove(submenu, i) end
end
menu_items_dirty = true
end)
end
end

-- menu data update callback
local function update_menu(name, items)
if not items or #items == 0 then return end
mp.unobserve_property(update_menu)

menu_items = items
dyn_menu_init()
dyn_menu_check(menu_items)
end

-- commit menu items if changed
local function update_menu_items()
if menu_items_dirty then
mp.set_property_native(menu_prop, menu_items)
menu_items_dirty = false
end
end

-- update menu items when idle, this reduces the update frequency
mp.register_idle(update_menu_items)

-- parse menu data when menu items ready
--
-- NOTE: to simplify the code, we only procss the first valid update
-- event and ignore the rest, this make it conflict with other
-- scripts that also update the menu data property.
if #menu_items > 0 then
dyn_menu_init()
dyn_menu_check(menu_items)
else
mp.observe_property(menu_prop, 'native', update_menu)
end

0 comments on commit 60edd00

Please sign in to comment.