This commit is contained in:
2026-03-31 23:30:57 -07:00
parent 6ef5b645a8
commit 57b595c1a4
7 changed files with 381 additions and 111 deletions

View File

@@ -1876,6 +1876,212 @@ local function _download_url_for_current_item(url)
return base, true
end
function M._extract_youtube_video_id(url)
url = trim(tostring(url or ''))
if url == '' then
return nil
end
local lower = url:lower()
local video_id = nil
if lower:match('youtu%.be/') then
video_id = url:match('youtu%.be/([^%?&#/]+)')
elseif lower:match('youtube%.com/watch') or lower:match('youtube%-nocookie%.com/watch') then
video_id = _extract_query_param(url, 'v')
elseif lower:match('youtube%.com/shorts/') or lower:match('youtube%-nocookie%.com/shorts/') then
video_id = url:match('/shorts/([^%?&#/]+)')
elseif lower:match('youtube%.com/live/') or lower:match('youtube%-nocookie%.com/live/') then
video_id = url:match('/live/([^%?&#/]+)')
elseif lower:match('youtube%.com/embed/') or lower:match('youtube%-nocookie%.com/embed/') then
video_id = url:match('/embed/([^%?&#/]+)')
end
video_id = trim(tostring(video_id or ''))
if video_id == '' or not video_id:match('^[%w_-]+$') then
return nil
end
return video_id
end
function M._suspicious_ytdl_format_reason(fmt, url, raw)
fmt = trim(tostring(fmt or ''))
url = trim(tostring(url or ''))
if fmt == '' then
return nil
end
local lower_fmt = fmt:lower()
if lower_fmt:match('^https?://') or lower_fmt:match('^rtmp') or (url ~= '' and fmt == url) then
return 'format string is a url'
end
local youtube_id = M._extract_youtube_video_id(url)
if youtube_id and fmt == youtube_id then
return 'format matches current youtube video id'
end
if type(raw) == 'table' and youtube_id then
local raw_id = trim(tostring(raw.id or ''))
if raw_id ~= '' and raw_id == youtube_id and fmt == raw_id then
return 'format matches raw youtube video id'
end
end
return nil
end
function M._clear_suspicious_ytdl_format_for_url(url, reason)
url = trim(tostring(url or ''))
if url == '' then
return false
end
local raw = mp.get_property_native('ytdl-raw-info')
local bad_props = {}
local bad_value = nil
local bad_reason = nil
local checks = {
{
prop = 'ytdl-format',
value = mp.get_property_native('ytdl-format'),
},
{
prop = 'file-local-options/ytdl-format',
value = mp.get_property('file-local-options/ytdl-format'),
},
{
prop = 'options/ytdl-format',
value = mp.get_property('options/ytdl-format'),
},
}
for _, item in ipairs(checks) do
local candidate = trim(tostring(item.value or ''))
local why = M._suspicious_ytdl_format_reason(candidate, url, raw)
if why then
if not bad_value then
bad_value = candidate
end
if not bad_reason then
bad_reason = why
end
bad_props[#bad_props + 1] = tostring(item.prop)
end
end
if #bad_props == 0 then
return false
end
pcall(mp.set_property, 'options/ytdl-format', '')
pcall(mp.set_property, 'file-local-options/ytdl-format', '')
pcall(mp.set_property, 'ytdl-format', '')
_lua_log(
'ytdl-format: cleared suspicious selector=' .. tostring(bad_value or '')
.. ' props=' .. table.concat(bad_props, ',')
.. ' reason=' .. tostring(bad_reason or reason or 'invalid')
.. ' url=' .. tostring(url)
)
return true
end
function M._prepare_ytdl_format_for_web_load(url, reason)
url = trim(tostring(url or ''))
if url == '' then
return false
end
if M._clear_suspicious_ytdl_format_for_url(url, reason) then
return true
end
local normalized_url = url:gsub('#.*$', '')
local base, query = normalized_url:match('^([^?]+)%?(.*)$')
if base then
local kept = {}
for pair in query:gmatch('[^&]+') do
local raw_key = pair:match('^([^=]+)') or pair
local key = tostring(_percent_decode(raw_key) or raw_key or ''):lower()
local keep = true
if key == 't' or key == 'start' or key == 'time_continue' or key == 'timestamp' or key == 'time' or key == 'begin' then
keep = false
elseif key:match('^utm_') then
keep = false
end
if keep then
kept[#kept + 1] = pair
end
end
if #kept > 0 then
normalized_url = base .. '?' .. table.concat(kept, '&')
else
normalized_url = base
end
end
local explicit_reload_url = normalized_url
explicit_reload_url = explicit_reload_url:gsub('^[%a][%w+%.%-]*://', '')
explicit_reload_url = explicit_reload_url:gsub('^www%.', '')
explicit_reload_url = explicit_reload_url:gsub('/+$', '')
explicit_reload_url = explicit_reload_url:lower()
local is_explicit_reload = (
explicit_reload_url ~= ''
and _skip_next_store_check_url ~= ''
and explicit_reload_url == _skip_next_store_check_url
)
local active_props = {}
local first_value = nil
local checks = {
{
prop = 'ytdl-format',
value = mp.get_property_native('ytdl-format'),
},
{
prop = 'file-local-options/ytdl-format',
value = mp.get_property('file-local-options/ytdl-format'),
},
{
prop = 'options/ytdl-format',
value = mp.get_property('options/ytdl-format'),
},
}
for _, item in ipairs(checks) do
local candidate = trim(tostring(item.value or ''))
if candidate ~= '' then
if not first_value then
first_value = candidate
end
active_props[#active_props + 1] = tostring(item.prop) .. '=' .. candidate
end
end
if #active_props == 0 then
return false
end
if is_explicit_reload then
_lua_log(
'ytdl-format: preserving explicit reload selector reason=' .. tostring(reason or 'on-load')
.. ' url=' .. tostring(url)
.. ' values=' .. table.concat(active_props, '; ')
)
return false
end
pcall(mp.set_property, 'options/ytdl-format', '')
pcall(mp.set_property, 'file-local-options/ytdl-format', '')
pcall(mp.set_property, 'ytdl-format', '')
_lua_log(
'ytdl-format: cleared stale selector=' .. tostring(first_value or '')
.. ' reason=' .. tostring(reason or 'on-load')
.. ' url=' .. tostring(url)
.. ' values=' .. table.concat(active_props, '; ')
)
return true
end
local function _normalize_url_for_store_lookup(url)
url = trim(tostring(url or ''))
if url == '' then
@@ -3742,6 +3948,8 @@ function M._apply_web_subtitle_load_defaults(reason)
return false
end
M._prepare_ytdl_format_for_web_load(target, reason or 'on-load')
local raw = M._build_web_ytdl_raw_options()
if raw and raw ~= '' then
pcall(mp.set_property, 'file-local-options/ytdl-raw-options', raw)
@@ -4664,22 +4872,35 @@ _show_format_list_osd = function(items, max_items)
end
local function _current_ytdl_format_string()
local url = _current_url_for_web_actions() or _current_target() or ''
local raw = mp.get_property_native('ytdl-raw-info')
-- Preferred: mpv exposes the active ytdl format string.
local fmt = trim(tostring(mp.get_property_native('ytdl-format') or ''))
if fmt ~= '' then
local suspicious_reason = M._suspicious_ytdl_format_reason(fmt, url, raw)
if fmt ~= '' and not suspicious_reason then
return fmt
elseif fmt ~= '' then
_lua_log('ytdl-format: ignoring suspicious current format source=ytdl-format value=' .. tostring(fmt) .. ' reason=' .. tostring(suspicious_reason))
end
-- Fallbacks: option value, or raw info if available.
local opt = trim(tostring(mp.get_property('options/ytdl-format') or ''))
if opt ~= '' then
suspicious_reason = M._suspicious_ytdl_format_reason(opt, url, raw)
if opt ~= '' and not suspicious_reason then
return opt
elseif opt ~= '' then
_lua_log('ytdl-format: ignoring suspicious current format source=options/ytdl-format value=' .. tostring(opt) .. ' reason=' .. tostring(suspicious_reason))
end
local raw = mp.get_property_native('ytdl-raw-info')
if type(raw) == 'table' then
if raw.format_id and tostring(raw.format_id) ~= '' then
return tostring(raw.format_id)
local raw_format_id = tostring(raw.format_id)
suspicious_reason = M._suspicious_ytdl_format_reason(raw_format_id, url, raw)
if not suspicious_reason then
return raw_format_id
end
_lua_log('ytdl-format: ignoring suspicious current format source=ytdl-raw-info.format_id value=' .. tostring(raw_format_id) .. ' reason=' .. tostring(suspicious_reason))
end
local rf = raw.requested_formats
if type(rf) == 'table' then
@@ -4690,7 +4911,12 @@ local function _current_ytdl_format_string()
end
end
if #parts >= 1 then
return table.concat(parts, '+')
local joined = table.concat(parts, '+')
suspicious_reason = M._suspicious_ytdl_format_reason(joined, url, raw)
if not suspicious_reason then
return joined
end
_lua_log('ytdl-format: ignoring suspicious current format source=ytdl-raw-info.requested_formats value=' .. tostring(joined) .. ' reason=' .. tostring(suspicious_reason))
end
end
end
@@ -4957,6 +5183,16 @@ local function _apply_ytdl_format_and_reload(url, fmt)
return
end
local suspicious_reason = M._suspicious_ytdl_format_reason(fmt, url, mp.get_property_native('ytdl-raw-info'))
if suspicious_reason then
pcall(mp.set_property, 'options/ytdl-format', '')
pcall(mp.set_property, 'file-local-options/ytdl-format', '')
pcall(mp.set_property, 'ytdl-format', '')
_lua_log('change-format: rejected suspicious format=' .. tostring(fmt) .. ' reason=' .. tostring(suspicious_reason) .. ' url=' .. tostring(url))
mp.osd_message('Invalid format selection', 3)
return
end
local pos = mp.get_property_number('time-pos')
local paused = mp.get_property_native('pause') and true or false
local media_title = trim(tostring(mp.get_property('media-title') or ''))