1
mirror of https://github.com/mpv-player/mpv synced 2024-11-14 22:48:35 +01:00
mpv/player/lua/auto_profiles.lua
CogentRedTester 712ef65e2a auto_profiles.lua: don't warn if profile-restore=default
The code assumed that the value of the profile-restore field is
"default" for such profiles, but the C implementation is such
that the field is intentionally omitted in such case.

This resulted in incorrectly trying to restore such profiles. However,
there are no stored values with profile-restore=default, so it only
resulted in a harmless warning, but otherwise behaved correctly (i.e.
nothing is restored).

Fix the condition by taking into account that missing field means
"default", so that it doesn't try to restore in such case, and
avoid the warning.
2022-01-30 15:13:10 +02:00

188 lines
5.7 KiB
Lua

-- Note: anything global is accessible by profile condition expressions.
local utils = require 'mp.utils'
local msg = require 'mp.msg'
local profiles = {}
local watched_properties = {} -- indexed by property name (used as a set)
local cached_properties = {} -- property name -> last known raw value
local properties_to_profiles = {} -- property name -> set of profiles using it
local have_dirty_profiles = false -- at least one profile is marked dirty
local pending_hooks = {} -- as set (keys only, meaningless values)
-- Used during evaluation of the profile condition, and should contain the
-- profile the condition is evaluated for.
local current_profile = nil
local function evaluate(profile)
msg.verbose("Re-evaluating auto profile " .. profile.name)
current_profile = profile
local status, res = pcall(profile.cond)
current_profile = nil
if not status then
-- errors can be "normal", e.g. in case properties are unavailable
msg.verbose("Profile condition error on evaluating: " .. res)
res = false
elseif type(res) ~= "boolean" then
msg.verbose("Profile condition did not return a boolean, but "
.. type(res) .. ".")
res = false
end
if res ~= profile.status then
if res == true then
msg.info("Applying auto profile: " .. profile.name)
mp.commandv("apply-profile", profile.name)
elseif profile.status == true and profile.has_restore_opt then
msg.info("Restoring profile: " .. profile.name)
mp.commandv("apply-profile", profile.name, "restore")
end
end
profile.status = res
profile.dirty = false
end
local function on_property_change(name, val)
cached_properties[name] = val
-- Mark all profiles reading this property as dirty, so they get re-evaluated
-- the next time the script goes back to sleep.
local dependent_profiles = properties_to_profiles[name]
if dependent_profiles then
for profile, _ in pairs(dependent_profiles) do
assert(profile.cond) -- must be a profile table
profile.dirty = true
have_dirty_profiles = true
end
end
end
local function on_idle()
-- When events and property notifications stop, re-evaluate all dirty profiles.
if have_dirty_profiles then
for _, profile in ipairs(profiles) do
if profile.dirty then
evaluate(profile)
end
end
end
have_dirty_profiles = false
-- Release all hooks (the point was to wait until an idle event)
while true do
local h = next(pending_hooks)
if not h then
break
end
pending_hooks[h] = nil
h:cont()
end
end
local function on_hook(h)
h:defer()
pending_hooks[h] = true
end
function get(name, default)
-- Normally, we use the cached value only
if not watched_properties[name] then
watched_properties[name] = true
mp.observe_property(name, "native", on_property_change)
cached_properties[name] = mp.get_property_native(name)
end
-- The first time the property is read we need add it to the
-- properties_to_profiles table, which will be used to mark the profile
-- dirty if a property referenced by it changes.
if current_profile then
local map = properties_to_profiles[name]
if not map then
map = {}
properties_to_profiles[name] = map
end
map[current_profile] = true
end
local val = cached_properties[name]
if val == nil then
val = default
end
return val
end
local function magic_get(name)
-- Lua identifiers can't contain "-", so in order to match with mpv
-- property conventions, replace "_" to "-"
name = string.gsub(name, "_", "-")
return get(name, nil)
end
local evil_magic = {}
setmetatable(evil_magic, {
__index = function(table, key)
-- interpret everything as property, unless it already exists as
-- a non-nil global value
local v = _G[key]
if type(v) ~= "nil" then
return v
end
return magic_get(key)
end,
})
p = {}
setmetatable(p, {
__index = function(table, key)
return magic_get(key)
end,
})
local function compile_cond(name, s)
local code, chunkname = "return " .. s, "profile " .. name .. " condition"
local chunk, err
if setfenv then -- lua 5.1
chunk, err = loadstring(code, chunkname)
if chunk then
setfenv(chunk, evil_magic)
end
else -- lua 5.2
chunk, err = load(code, chunkname, "t", evil_magic)
end
if not chunk then
msg.error("Profile '" .. name .. "' condition: " .. err)
chunk = function() return false end
end
return chunk
end
local function load_profiles()
for i, v in ipairs(mp.get_property_native("profile-list")) do
local cond = v["profile-cond"]
if cond and #cond > 0 then
local profile = {
name = v.name,
cond = compile_cond(v.name, cond),
properties = {},
status = nil,
dirty = true, -- need re-evaluate
has_restore_opt = v["profile-restore"] and v["profile-restore"] ~= "default"
}
profiles[#profiles + 1] = profile
have_dirty_profiles = true
end
end
end
load_profiles()
if #profiles < 1 and mp.get_property("load-auto-profiles") == "auto" then
-- make it exit immediately
_G.mp_event_loop = function() end
return
end
mp.register_idle(on_idle)
for _, name in ipairs({"on_load", "on_preloaded", "on_before_start_file"}) do
mp.add_hook(name, 50, on_hook)
end
on_idle() -- re-evaluate all profiles immediately